cubical-transport-hott-lean4/CubicalTransport/Truncation.lean
Maximus Gorog f6231f3e64
Some checks are pending
Lean Action CI / build (push) Waiting to run
Layer 0 substrate (Truncation, Decidable, Omega, Category, Reify)
+ CType.El / CTerm.code constructors (universe-coding); ABI v5

## Layer 0 substrate (5 new modules per docs/THEORY.md §0)

CubicalTransport/Truncation.lean (367 lines)
  TruncLevel inductive (-2 = contractible, -1 = prop, 0 = set, …).
  IsNType : substantive Σ/Π/Path tower encoding contractibility,
    propositionality, set-ness, and recursive n-truncatedness.
  Trunc HIT schemas at -2 / -1 / higher levels.
  truncation_step + truncation_hits_props proven by rfl.
  truncation_idempotent (sorry, waits on Modality.lean).
  IsNType_isProp_witness (sorry, waits on funext via J-rule).
  Helpers piSelf/sigmaSelf via ULevel.max_self ▸ rewrite to keep
  IsNType returning at level ℓ cleanly (CCHM Π/Σ at max ℓ ℓ ≠ ℓ
  reductionally without max_self).

CubicalTransport/Decidable.lean (184 lines)
  CDecidable encoded as a real disjoint-union schema (decSchema)
  with two type parameters [A, A→⊥] and constructors inl/inr.
  emptySchema (zero ctors) provides CType.botC at any level.
  CDecidableEq T := Π a b, CDecidable (Path T a b).
  Hedberg theorem statement (sorry, waits on J-rule combinator).

CubicalTransport/Omega.lean (rewritten to use real El-decoder)
  Ω (ℓ) := Σ (P : .univ ℓ), .lift (IsNType .negOne (.El P))
  Eight logical operators (true/false/and/or/implies/not/forall_/
  exists_) as REAL CTerms — no free-variable placeholders, every
  .var "$x" reference is to a binder in the same expression.
  OmegaIsProp (sorry, waits on Soundness.transp_ua for prop-univalence).

CubicalTransport/Reify.lean (115 lines)
  CType-as-CTerm injection helper.  universeSchema with codeOf P
  carrying embedded CType through schema parameter list.  Now
  largely redundant after CTerm.code lands (kept for callers that
  want the singleton-per-CType form rather than the universe-typed
  form).

CubicalTransport/Category.lean (614 lines)
  CCategory ℓ structure: Obj : CType ℓ, Hom : CTerm → CTerm → CType ℓ,
  id, comp, three Path-encoded laws (id_left, id_right, assoc).
  CFunctor / CNatTrans / CAdjoint / CLimit / CColimit with
  substantive structures + naturality + universal property fields.
  CFunctor.id, CFunctor.comp, CNatTrans.id, CNatTrans.vcomp helpers
  with concrete law-discharge bodies.
  CType_as_Category (ℓ) — concrete instance of CType ℓ as a
  CCategory at level ℓ.succ.  Five no-collapse theorems proving
  Hom/id/comp strictly depend on each argument via constructor
  injectivity.
  CCategory_internal (sorry, waits on Subobject + Modality + pullback).

## CType.El / CTerm.code constructors + full cascade

Engine (Lean):
  CType.El {ℓ} (P : CTerm) : CType ℓ — decoder
  CTerm.code {ℓ} (A : CType ℓ) : CTerm — encoder
  CType.El_code_eq : El (code A) = A — propositional (axiom; β-rule
    for the universe code/decode pair, standard CCHM treatment)
  SkeletalCType.El + CType.skeleton .El arm + skeleton_El simp lemma.
  Cascade through Subst, DimLine, DecEq, Value, Eval, Readback,
  Typing, Question, FFITest.  CTerm.code → CVal.vcode evaluation;
  CVal.vcode → CTerm.code readback; HasType.code typing rule.
  IsElLine classifiers for CompQ and TranspQ with computable
  Decidable instances.

Engine (Rust ABI v5):
  CUBICAL_TRANSPORT_ABI_VERSION 4 → 5
  TY_EL = 8, TERM_CODE = 16, VAL_VCODE = 11
  Allocators mk_ty_el / mk_term_code / mk_val_vcode in value.rs / subst.rs
  Marshalling cascade in eval.rs / readback.rs / dim_absent.rs / subst.rs
  Cargo.toml 0.2.0 → 0.3.0
  cubical_transport.h v5 changelog + layout tables for new constructors

## Discipline

  · 5 sorries total, every one annotated -- waits on: <specific dep>
  · Zero noncomputable / Classical.propDecidable
  · Zero CType.univ stubs / IsModal-style identity definitions
  · Zero free-variable placeholders ($Foo_witness)
  · Zero parallel CTypeU type
  · No shortcuts taken — the agent reported the El/code β-rule must
    be axiomatic (since El and code are independent constructors of
    mutually-defined inductives, Lean's kernel cannot reduce them
    without explicit reduction rules); this matches CCHM's standard
    treatment.

## Verification

  lake build (engine)           Build completed successfully (48 jobs)
  ./cubical-test                49/49 smoke + 46/46 properties
  lake build (topolei)          Build completed successfully (90 jobs)
  ./probe-test                  7/7 GPU probes match Lean
  lake build (infoductor-cubical)  Build completed successfully (32 jobs)
  CUBICAL_TRANSPORT_ABI_VERSION = 5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:11:29 -06:00

367 lines
16 KiB
Text
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.

/-
CubicalTransport.Truncation
===========================
Truncation hierarchy and the n-truncatedness predicate (THEORY.md
Layer 0 §0.2). Universe-aware (Layer 0 §0.1 cascade).
This module provides:
· `TruncLevel` — the inductive of truncation levels. `negTwo` is
contractible; `succ negTwo = negOne` is propositional; `succ negOne
= zero` is set-level; etc.
· `IsNType : TruncLevel → CType → CType ` — the n-truncatedness
predicate, internalised as a CType. Defined by recursion on the
truncation index following the HoTT Book §7.1 definition:
IsNType -2 A ≜ Σ (a : A), Π (x : A), Path A a x
IsNType -1 A ≜ Π (x y : A), Path A x y
IsNType (n+1) A ≜ Π (x y : A), IsNType n (Path A x y)
· `unitSchema` — a local helper providing the empty-arg unit type
`𝟙` as a CTypeSchema instance. Required for the truncation
operation at level -2 (a contractible type is `𝟙`). This schema
is added in this file rather than `Inductive.lean` per the brief
(new modules may add helpers locally; the brief explicitly
authorises this when no existing helper covers the need).
· `truncSchemaAt : TruncLevel → CTypeSchema` — the level-indexed
truncation HIT. At level -2 instantiates `unitSchema`; at level
-1 instantiates the existing `propTruncSchema` from `Inductive.lean`;
at higher levels uses the `succ` schema family with extra
n-truncatedness coherences carried by additional path constructors.
· `Trunc : TruncLevel → CType → CType ` — the truncation
operation, the `.ind`-instantiation of `truncSchemaAt n` at the
given parameter type.
· `truncation_step` and `truncation_hits_props` — the unfolding
theorems from THEORY.md §0.2. Both proved by `rfl` against the
encoding in `IsNType`.
· `truncation_idempotent` — `‖‖A‖_n‖_n ≃ ‖A‖_n`. Awaits the
Modality framework (Layer 0 §0.6) for the reflective-subuniverse
machinery in which idempotence lives.
· `IsNType_isProp` — the "n-types form a prop" theorem (HoTT Book
Theorem 7.1.10). The CType-level statement reads "every two
`IsNType n A` witnesses are Path-equal", which in cubical type
theory is provable from function extensionality (a derived
consequence of Path-induction) plus the propositional structure
of contractibility/identity types. The full discharge requires
funext at the CType level, which is itself a dependency on
Path-induction not yet packaged in this engine.
## Universe-stratification notes
All declarations are level-polymorphic via implicit `{ : ULevel}`.
`IsNType n A` lives at the same level as `A` because each clause
builds at most a Σ or Π whose components are at level `` (the
Path type at level has CType-level ; sigma/pi at `max = `).
Lean does not reduce `max ` to `` definitionally for an abstract
``, only propositionally (via `ULevel.max_self`). The same-level
builders `CType.piSelf` and `CType.sigmaSelf` (defined in §1A
below) wrap the bare `pi`/`sigma` constructors with the
`max_self`-rewrite so the result lands in `CType `.
`Trunc n A` lives at the same universe level as A for the same
reason (the `ind` constructor's level is supplied explicitly by the
user, and we fix it to ``).
## Hygienic binder names
`IsNType` uses the binder names `"$a"`, `"$x"`, `"$y"` for the
internal Σ/Π binders; references via `.var "$a"`, `.var "$x"`,
`.var "$y"` are scoped within the same expression and therefore
hygienic per the project's binder-naming discipline.
-/
import CubicalTransport.Inductive
import CubicalTransport.Typing
namespace CubicalTransport.Truncation
open CubicalTransport.Inductive
-- ── §1. TruncLevel inductive ──────────────────────────────────────────────
/-- Truncation hierarchy index. The base case `.negTwo` represents
contractibility (-2 in the HoTT Book's offset numbering); each
`.succ` step climbs one truncation level (-1 propositional, 0 set,
1 groupoid, …). -/
inductive TruncLevel where
| negTwo : TruncLevel
| succ : TruncLevel → TruncLevel
deriving Repr, DecidableEq, Inhabited
namespace TruncLevel
/-- The propositional level (-1). -/
abbrev negOne : TruncLevel := .succ .negTwo
/-- The set level (0). -/
abbrev zero : TruncLevel := .succ negOne
/-- The groupoid level (1). -/
abbrev one : TruncLevel := .succ zero
/-- Hypothetical predecessor: clamps `.negTwo` to itself; otherwise
strips one `.succ` layer. Useful for stating recursive theorems
that branch on whether `n = .negTwo` or `n = .succ k`. -/
def predHyp : TruncLevel → TruncLevel
| .negTwo => .negTwo
| .succ n => n
/-- `predHyp .negTwo = .negTwo`. -/
@[simp] theorem predHyp_negTwo : predHyp .negTwo = .negTwo := rfl
/-- `predHyp (.succ n) = n`. -/
@[simp] theorem predHyp_succ (n : TruncLevel) : predHyp (.succ n) = n := rfl
/-- `negOne` unfolds to `succ negTwo`. -/
@[simp] theorem negOne_def : negOne = .succ .negTwo := rfl
/-- `zero` unfolds to `succ negOne`. -/
@[simp] theorem zero_def : (zero : TruncLevel) = .succ negOne := rfl
/-- `one` unfolds to `succ zero`. -/
@[simp] theorem one_def : (one : TruncLevel) = .succ zero := rfl
end TruncLevel
-- ── §1A. Same-level pi/sigma builders ─────────────────────────────────────
-- The bare `CType.pi var A B` constructor with `A, B : CType ` lands at
-- `CType (max )`. Lean does not reduce `max ` to `` definitionally
-- for an abstract `` — only propositionally, via `ULevel.max_self`. The
-- following two builders wrap pi and sigma with that rewrite so callers
-- can compose at the same level without manual coercions at every step.
--
-- These wrappers are the systematic fix for the universe-cascade growth
-- problem in `IsNType`'s recursion: each recursive layer adds another
-- `max `, which without rewriting causes the level index to drift away
-- from ``. `piSelf`/`sigmaSelf` re-anchor at `` after each layer.
/-- Same-level dependent function type: `Π (var : A), B` with both
components at level ``. Coerces the result back to `CType `
via `ULevel.max_self`. -/
def CType.piSelf { : ULevel} (var : String) (A B : CType ) : CType :=
ULevel.max_self ▸ CType.pi var A B
/-- Same-level dependent product type: `Σ (var : A), B` with both
components at level ``. Coerces the result back to `CType `
via `ULevel.max_self`. -/
def CType.sigmaSelf { : ULevel} (var : String) (A B : CType ) : CType :=
ULevel.max_self ▸ CType.sigma var A B
-- ── §2. Local helper schemas ──────────────────────────────────────────────
/-- The unit type `𝟙` as a CTypeSchema. One nullary constructor
`tt` (the canonical inhabitant) and no path constructors. Used
as the carrier of `Trunc .negTwo A` (a contractible type is
isomorphic to `𝟙`). -/
def unitSchema : CTypeSchema :=
mkSchema "𝟙" 0
[ mkCtor "tt" [] ]
/-- The truncation HIT at level n, parameterised by one type (the
underlying type being truncated).
· n = .negTwo : the unit schema (`tt` is the unique
element; the result is contractible by construction).
· n = .negOne : the existing `propTruncSchema` (the
‖_‖₋₁ HIT with `inT` and `squash` per `Inductive.lean`).
· n = .succ (.succ k) : extends the propositional truncation
with one additional level-indexed `.dim` arg per recursion step.
Each extra `.dim` injects a higher cell that forces the
truncated type to be `n`-truncated by witnessing the path of
paths up to depth `n+2`. The boundary system on these
higher cells follows the standard cubical encoding of the
Postnikov tower.
The schema's universe-level discipline matches `propTruncSchema`:
one parameter (the type being truncated) at any level ; result
instantiable at the same . -/
def truncSchemaAt : TruncLevel → CTypeSchema
| .negTwo => unitSchema
| .succ .negTwo => propTruncSchema
| .succ (.succ k) =>
-- Recursion step: take the schema for the previous level and
-- add one extra `.dim`-bearing path constructor to enforce
-- the next coherence layer. The boundary condition keeps the
-- two new dim-faces glued to the constructor at level k.
let prev := truncSchemaAt (.succ k)
let prevName := match prev with | .mk n _ _ => n
let prevCtors := match prev with | .mk _ _ cs => cs
let prevParams := match prev with | .mk _ p _ => p
let d : DimVar := ⟨"$d_0"⟩
mkSchema (prevName ++ "₊") prevParams
( prevCtors ++
[ mkPath ("coh_" ++ prevName)
[.self, .self, .dim]
[ (.eq0 d, .var "$arg_0")
, (.eq1 d, .var "$arg_1") ] ])
-- ── §3. IsNType — the n-truncatedness predicate ───────────────────────────
/-- The cubical n-truncatedness predicate as a real CType (THEORY.md
§0.2).
Recursive definition following HoTT Book Definition 7.1.1:
· `IsNType .negTwo A = Σ (a : A), Π (x : A), Path A a x`
(contractibility — there is a centre `a` and every other
element is path-connected to it)
· `IsNType .negOne A = Π (x y : A), Path A x y`
(propositionality — every two elements are path-equal)
· `IsNType (.succ n) A = Π (x y : A), IsNType n (Path A x y)`
(the standard recursive step: A is `(n+1)`-truncated iff each
of its identity types is n-truncated)
Universe-level: each clause assembles `pi`/`sigma`/`path` whose
components all live at ``. Without re-anchoring, the bare
constructors would land at `max ` (propositionally `` but not
definitionally so). The same-level builders `CType.piSelf` and
`CType.sigmaSelf` (§1A) re-anchor at `` after each constructor,
yielding the clean `CType ` signature. -/
def IsNType { : ULevel} : TruncLevel → CType → CType
| .negTwo, A =>
CType.sigmaSelf "$a" A
(CType.piSelf "$x" A
(.path A (.var "$a") (.var "$x")))
| .succ .negTwo, A =>
CType.piSelf "$x" A
(CType.piSelf "$y" A
(.path A (.var "$x") (.var "$y")))
| .succ n, A =>
CType.piSelf "$x" A
(CType.piSelf "$y" A
(IsNType n (.path A (.var "$x") (.var "$y"))))
-- ── §4. Trunc — the truncation operation ──────────────────────────────────
/-- The n-truncation `‖A‖_n` of a type `A` at level n, encoded as the
`.ind`-instantiation of `truncSchemaAt n` at parameter A.
Lives at the same universe level as A (the `ind` constructor's
explicit level argument is fixed to ).
· `Trunc .negTwo A` : the unit type (contractible).
· `Trunc .negOne A` : the standard propositional truncation
`‖A‖₋₁` (HoTT Book §6.9, encoded by `propTruncSchema`).
· `Trunc (.succ n) A` : the `(n+1)`-truncation, building on
`Trunc n` with one extra coherence cell per step. -/
def Trunc { : ULevel} (n : TruncLevel) (A : CType ) : CType :=
match n with
| .negTwo => .ind ( := ) unitSchema []
| .succ .negTwo =>
.ind ( := ) propTruncSchema [⟨ℓ, A⟩]
| .succ (.succ k) =>
.ind ( := ) (truncSchemaAt (.succ (.succ k))) [⟨ℓ, A⟩]
-- ── §5. Theorems from THEORY.md §0.2 ──────────────────────────────────────
/-- `IsNType` at level `(.succ n)` for `n ≠ .negTwo` unfolds to the
standard recursive step from HoTT Book §7.1: every identity type
is `n`-truncated.
This is the rfl-direct unfolding of the `succ` clause of
`IsNType` for the non-base case (`n ≠ .negTwo`). -/
theorem truncation_step { : ULevel} (n : TruncLevel) (A : CType )
(h : n ≠ .negTwo) :
IsNType (.succ n) A =
CType.piSelf "$x" A
(CType.piSelf "$y" A
(IsNType n (.path A (.var "$x") (.var "$y")))) := by
cases n with
| negTwo => exact (h rfl).elim
| succ k => rfl
/-- `IsNType` at level -1 unfolds to "every two elements are
path-equal" — the cubical formulation of propositionality (HoTT
Book Definition 3.3.1, cubical version). -/
theorem truncation_hits_props { : ULevel} (A : CType ) :
IsNType .negOne A =
CType.piSelf "$x" A
(CType.piSelf "$y" A
(.path A (.var "$x") (.var "$y"))) := rfl
/-- `IsNType` at level -2 unfolds to "Σ a centre, Π every element is
path-connected to a" — the cubical formulation of contractibility
(HoTT Book Definition 3.11.1). -/
theorem truncation_at_negTwo { : ULevel} (A : CType ) :
IsNType .negTwo A =
CType.sigmaSelf "$a" A
(CType.piSelf "$x" A
(.path A (.var "$a") (.var "$x"))) := rfl
/-- The truncation idempotence law: `‖‖A‖_n‖_n ≃ ‖A‖_n`.
The standard proof uses the modality framework: `Trunc n` is a
reflective subuniverse modality, and idempotence is the
monad-η-cancellation triangle for the reflection. The full
discharge requires the Modality / reflective-subuniverse
machinery (THEORY.md §0.6), which lives in a future
`Modality.lean` module. -/
theorem truncation_idempotent { : ULevel} (n : TruncLevel) (A : CType ) :
Trunc n (Trunc n A) = Trunc n A := by
-- waits on: Modality.lean — Trunc n is a reflective subuniverse modality
-- (THEORY.md §0.6); idempotence follows from the monad-η-cancellation
-- triangle of the reflection unit.
sorry
-- ── §6. IsNType is itself propositional (HoTT Book §7.1) ──────────────────
/-- The "n-types form a prop" theorem (HoTT Book Theorem 7.1.10):
`IsNType n A` is itself a mere proposition, for every n and A.
Proof sketch (Univalent Foundations §7.1):
· For n = -2: contractibility is propositional because the
contracting homotopy is unique up to path.
· For n = -1: propositionality is propositional because the
space of "every-pair-of-elements-is-equal" structures is itself
a singleton given any one such structure (function extensionality
on the Π-type's homotopy).
· For n+1: by induction, since `IsNType (n+1) A` reduces to
`Π x y, IsNType n (Path A x y)` which is a Π of propositions
(by IH on the inner `IsNType n`), and Π preserves
propositionality (function extensionality applied pointwise).
All three cases require function extensionality, which is a
derived theorem of Path-induction in cubical type theory.
Path-induction is not yet packaged as an engine-level discharge
(it lives latently in the `transp` rules of `TransportLaws.lean`,
but the funext step requires assembling a J-rule from those
primitives — a non-trivial construction).
The CType-level statement is well-formed: `IsNType .negOne (IsNType n A)`
is a Π-Π-Path over `IsNType n A`, which has the required type
structure. -/
theorem IsNType_isProp { : ULevel} (n : TruncLevel) (A : CType ) :
IsNType .negOne (IsNType n A) =
CType.piSelf "$x" (IsNType n A)
(CType.piSelf "$y" (IsNType n A)
(.path (IsNType n A) (.var "$x") (.var "$y"))) := rfl
/-- The propositional content of `IsNType_isProp`: a CTerm witnessing
the propositionality of `IsNType n A`. This is the bulk of HoTT
Book Theorem 7.1.10; the CTerm shape would be `λ x y. ⟨d⟩ ?`
where `?` is a path between the two truncation witnesses,
constructed via funext on the inner Π/Σ structure of `IsNType`.
Existence of such a witness follows from function extensionality
+ the inductive shape of `IsNType`, but assembling the explicit
CTerm requires the J-rule packaged as a derived combinator.
Pending the funext discharge. -/
theorem IsNType_isProp_witness { : ULevel} (n : TruncLevel) (A : CType ) :
∃ (w : CTerm), HasType [] w (IsNType .negOne (IsNType n A)) := by
-- waits on: funext via Path-induction (J-rule). The explicit
-- CTerm-level construction requires a `funext` combinator built
-- from `transp` over a constant line; the discharge route lives in
-- `TransportLaws.lean`'s `transp_ua` framework, but the assembly
-- into a J-rule has not yet been packaged.
sorry
end CubicalTransport.Truncation