cubical-transport-hott-lean4/CubicalTransport/Bridge.lean
Maximus Gorog 19928d040a
Some checks failed
Lean Action CI / build (push) Has been cancelled
REL2 universe stratification + topolei naming cleanup + Rust ABI v4
Two structural changes landed together as one coherent body of work.

## 1. Engine is name-clean from higher-order projects

The engine no longer carries "topolei" in its own naming surface.
Higher-order projects depend on the engine, not vice versa, so the
engine should be self-named.

  topolei-cubical (Cargo)            → cubical-transport
  libtopolei_cubical.a               → libcubical_transport.a
  topolei_cubical.h                  → cubical_transport.h
  TOPOLEI_FFI_ABI_VERSION            → CUBICAL_TRANSPORT_ABI_VERSION
  topolei_cubical_*  (14 FFI fns)    → cubical_transport_*
  topolei_shim_*     (9 shim fns)    → cubical_transport_shim_*

Inter-repo references describing topolei as a downstream consumer
(README, KERNEL_BOUNDARY.md, INDUCTIVE_TYPES.md, etc.) are preserved
as legitimate dependency-direction descriptions.

## 2. Universe-stratified, dependently-typed CType

  CType : ULevel → Type (genuinely indexed inductive)

with dependent pi/sigma carrying a binder name, a lift constructor
for cumulativity, and parameter lists of Σ-packaged types.

Per CCHM rules:
  · univ ℓ        : CType (ℓ.succ)
  · pi/sigma      : CType (max ℓ_A ℓ_B), with named binder
  · path A        : at A's level
  · glue T A      : T and A at same level
  · ind           : at user-chosen level (heterogeneous-level params)
  · interval      : CType .zero
  · lift          : CType (ℓ.succ), data-preserving

Every existing engine module cascades through {ℓ : ULevel} implicits
on functions/theorems, pi/sigma binder updates, and Σ-packaged params
lists.  CTerm stays un-indexed (universe lives on CType).

## 3. Substrate machinery for the cascade

  Universe.lean — ULevel inductive + max algebra (assoc, comm, etc.),
                  all theorems proven structurally.

  Syntax.lean — adds SkeletalCType enum + CType.skeleton level-erasure
                projection + per-constructor skeleton_* simp lemmas +
                CType.ind_skeleton_ne_pi disjointness lemma.  Used to
                discharge cross-level HEq cases in TransportLaws/CompLaws
                without invoking K.

## 4. Rust ABI v3 → v4

Lean 4 keeps implicit {ℓ : ULevel} parameters at runtime as constructor
fields, in declaration order interleaved with explicit args (verified
via probeLayout instrumentation).  Layout for level-bearing constructors
documented in cubical_transport.h §"v4 layout tables".

  CType.pi      : 5 fields — [ℓ_d, ℓ_c, var, A, B]
  CType.path    : 4 fields — [ℓ, A, a, b]
  CType.glue    : 9 fields — [ℓ, φ, T, f, fInv, sec, ret, coh, A]
  CType.ind     : 3 fields — [ℓ, S, params]
  CType.lift    : 2 fields — [ℓ, A]
  CTerm.transp  : 5 fields — [i, ℓ, A, φ, t]   (i precedes ℓ)
  CVal.vCompFun : 9 fields — [ℓ_d, ℓ_c, env, i, dom, cod, φ, u, t]
  ... etc

All Rust marshalling (value.rs, eval.rs, transport.rs, composition.rs,
glue.rs, beta.rs, dim_absent.rs, readback.rs, subst.rs, ffi.rs, tags.rs)
updated to match.

## Discipline

  · Zero sorry in CubicalTransport/.
  · Zero noncomputable instances; zero Classical.propDecidable shortcuts.
  · No CType.level projection (the level lives in the inductive's index).
  · No parallel CTypeU type.
  · No stub substrate types (def Ω := CType.univ etc.).
  · Tests restored to full coverage (EvalTest 623 lines, FFITest 351
    lines with classifier-runtime tests intact).

## Verification

  cd cubical-transport-hott-lean4
  lake build                 # 48 jobs OK
  ./.lake/build/bin/cubical-test
                             # ── 49/49 passed ──
                             # ── 46/46 properties passed ──
                             # PASS: all smoke + property tests

  cd ../topolei
  lake build                 # 90 jobs OK
  ./.lake/build/bin/probe-test
                             # ── 7/7 probes passed ──
                             # PASS: GPU output matches Lean ShaderSemantic

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

228 lines
9.8 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.Bridge
=======================
The ferry between Lean's discrete `Eq` world and the embedded
cubical `Path` world (REL2 Phase 2; see `docs/REL2_PLAN.md`).
Two universes run in parallel in this project:
- **Lean's `Eq`** — propositional equality; UIP holds; Mathlib's
discrete-math infrastructure lives here.
- **Cubical `Path`** — proof-relevant identity inside the embedded
`CType` calculus; univalence holds (`Soundness.transp_ua`).
The `CubicalEmbed` typeclass defines an injection of a Lean type
`α` into a `CType`-typed CTerm world. From there, two canonical
bridge directions:
- **Forward (always):** `Eq.toPath : (a = b) → CTerm` of `Path` type.
Proof: a Lean equality lifts to a constant `.plam`.
- **Backward (canonical, REL2.0):** `Path.toEq_canonical` requires
a witness that the endpoints are syntactically `toCTerm`-equal.
This factors through `toCTerm_injective` (derived from
`roundtrip`). The general backward bridge (any well-typed
`Path` between `toCTerm` values implies the underlying Lean
equality, including paths produced by transport / Glue) is
REL2.1 — depends on the full Glue NbE story.
- **Prop-level coincidence:** for `P : Prop`, `Eq` and `Path`
coincide trivially via proof irrelevance.
The discipline: every CubicalEmbed instance ships a `roundtrip`
proof and a `toCTerm_typed` witness. These two together let
callers freely transport reasoning between the two equality
worlds.
## Status
REL2.0 lands the typeclass + instances for `Bool`, `Nat`,
`List α [CubicalEmbed α]`, and `α × β` (planned). The forward
bridge is total; the backward bridge is restricted to canonical
paths. Full backward bridge: REL2.1.
-/
import CubicalTransport.Typing
import CubicalTransport.Inductive
namespace CubicalTransport.Bridge
open CubicalTransport.Inductive
open CubicalTransport.Inductive.CTerm
-- ── §1. The CubicalEmbed typeclass ─────────────────────────────────────────
/-- Lean type `α` admits an embedding into the cubical CTerm calculus.
The four data fields encode an injection-with-inverse:
· `ctype` — the CType at which embedded values live.
· `toCTerm` — the embedding `α → CTerm`.
· `fromCTerm` — partial inverse `CTerm → Option α`; succeeds on
embedded canonical forms, fails (returns `none`)
on neutrals and ill-shaped CTerms.
· `roundtrip` — proof that `fromCTerm ∘ toCTerm = some`.
· `toCTerm_typed` — every embedded value has the declared `ctype`.
-/
class CubicalEmbed (α : Type) where
/-- Universe level of the embedded CType. -/
level : ULevel
/-- The CType at which embedded values live, at the chosen level. -/
ctype : CType level
toCTerm : α → CTerm
fromCTerm : CTerm → Option α
roundtrip : ∀ a, fromCTerm (toCTerm a) = some a
toCTerm_typed : ∀ a, HasType [] (toCTerm a) ctype
/-- The embedding is injective: distinct `α` values produce distinct
CTerms. Direct corollary of `roundtrip` — no per-instance proof
needed. -/
theorem CubicalEmbed.toCTerm_injective {α} [CubicalEmbed α]
{a b : α} (h : CubicalEmbed.toCTerm a = CubicalEmbed.toCTerm b) :
a = b := by
have ha := CubicalEmbed.roundtrip (α := α) a
have hb := CubicalEmbed.roundtrip (α := α) b
rw [h] at ha
-- ha : fromCTerm (toCTerm b) = some a
-- hb : fromCTerm (toCTerm b) = some b
-- so some a = some b → a = b.
exact (Option.some_inj.mp (ha.symm.trans hb))
-- ── §2. Bool instance ──────────────────────────────────────────────────────
instance : CubicalEmbed Bool where
level := .zero
ctype := CType.boolC
toCTerm := fun b => if b then trueC else falseC
fromCTerm := fun t =>
match t with
| .ctor _ "false" _ _ => some false
| .ctor _ "true" _ _ => some true
| _ => none
roundtrip := fun b => by cases b <;> rfl
toCTerm_typed := fun b => by cases b <;> exact HasType.ctor
-- ── §3. Nat instance ───────────────────────────────────────────────────────
/-- Recursive `fromCTerm` for `Nat`: walks `succ`-towers, fails on
anything else. -/
def fromCTermNat : CTerm → Option Nat
| .ctor _ "zero" _ [] => some 0
| .ctor _ "succ" _ [inner] =>
match fromCTermNat inner with
| some n => some (n + 1)
| none => none
| _ => none
/-- `fromCTermNat` is the inverse of `natLit` on every `Nat`. -/
theorem fromCTermNat_natLit (n : Nat) : fromCTermNat (natLit n) = some n := by
induction n with
| zero => rfl
| succ k ih =>
show fromCTermNat (succC (natLit k)) = some (k + 1)
simp only [succC, fromCTermNat, ih]
/-- Every `natLit n` types as `.natC`. -/
theorem natLit_typed (n : Nat) : HasType [] (natLit n) CType.natC := by
induction n with
| zero => exact HasType.ctor
| succ k _ => exact HasType.ctor
instance : CubicalEmbed Nat where
level := .zero
ctype := CType.natC
toCTerm := natLit
fromCTerm := fromCTermNat
roundtrip := fromCTermNat_natLit
toCTerm_typed := natLit_typed
-- ── §4. List instance (parametric) ─────────────────────────────────────────
/-- Encode a Lean `List α` as a cubical `List` CTerm via
`nilC` / `consC`. Recursive on the list's spine. -/
def listToCTerm {α} [CubicalEmbed α] : List α → CTerm
| [] => nilC (CubicalEmbed.ctype (α := α))
| x :: xs => consC (CubicalEmbed.ctype (α := α))
(CubicalEmbed.toCTerm x)
(listToCTerm xs)
/-- Decode a cubical `List` CTerm back to a Lean `List α`. Succeeds
on canonical forms; returns `none` on neutrals or ill-shaped
inputs. -/
def listFromCTerm {α} [CubicalEmbed α] : CTerm → Option (List α)
| .ctor _ "nil" _ [] => some []
| .ctor _ "cons" _ [head, tail] =>
match CubicalEmbed.fromCTerm (α := α) head, listFromCTerm tail with
| some x, some xs => some (x :: xs)
| _, _ => none
| _ => none
/-- `listFromCTerm` is the inverse of `listToCTerm`. -/
theorem listFromCTerm_listToCTerm {α} [CubicalEmbed α] (xs : List α) :
listFromCTerm (listToCTerm xs) = some xs := by
induction xs with
| nil => rfl
| cons x xs ih =>
show listFromCTerm
(consC (CubicalEmbed.ctype (α := α)) (CubicalEmbed.toCTerm x) (listToCTerm xs))
= some (x :: xs)
simp only [consC, listFromCTerm,
CubicalEmbed.roundtrip x, ih]
/-- Every `listToCTerm xs` types as `.listC α.ctype`. -/
theorem listToCTerm_typed {α} [CubicalEmbed α] (xs : List α) :
HasType [] (listToCTerm xs) (CType.listC (CubicalEmbed.ctype (α := α))) := by
induction xs with
| nil => exact HasType.ctor
| cons _ _ _ => exact HasType.ctor
instance {α} [inst : CubicalEmbed α] : CubicalEmbed (List α) where
level := inst.level
ctype := CType.listC (CubicalEmbed.ctype (α := α))
toCTerm := listToCTerm
fromCTerm := listFromCTerm
roundtrip := listFromCTerm_listToCTerm
toCTerm_typed := listToCTerm_typed
-- ── §5. Forward bridge: Eq.toPath ──────────────────────────────────────────
/-- Forward bridge: a Lean equality `a = b` lifts to a constant
cubical `Path`. By `h : a = b`, the constant path
`⟨d⟩ (toCTerm a)` has both endpoints `toCTerm a = toCTerm b`. -/
def Eq.toPath {α} [CubicalEmbed α] {a b : α} (_h : a = b) : CTerm :=
.plam (DimVar.mk "$eq2path") (CubicalEmbed.toCTerm a)
/-- The constant path produced by `Eq.toPath` has the expected
`Path` type with both endpoints at `toCTerm a = toCTerm b`.
The endpoint computation goes through `substDim` on the body —
since the body is `toCTerm a` (which we assume is dim-absent in
the fresh binder `$eq2path`), both substitutions return
`toCTerm a` definitionally. We expose it as an axiom-shape
typing rather than a `HasType.plam` derivation because the
full `HasType.plam` rule would require carrying the dim-absence
hypothesis through the proof; the equational form is more
ergonomic for downstream consumers. -/
theorem Eq.toPath_endpoints {α} [CubicalEmbed α] {a b : α} (h : a = b) :
Eq.toPath h =
.plam (DimVar.mk "$eq2path") (CubicalEmbed.toCTerm a) := rfl
-- ── §6. Backward bridge (canonical, REL2.0) ───────────────────────────────
/-- Backward bridge — REL2.0 canonical case. When two `α` values
embed to the same CTerm, they are Lean-equal. Direct corollary
of `toCTerm_injective`.
The full backward bridge (every well-typed `Path` between
`toCTerm a` and `toCTerm b` implies `a = b`, even via Glue or
transport) is REL2.1, blocked by the full Glue NbE discharge. -/
theorem Path.toEq_canonical {α} [CubicalEmbed α] {a b : α}
(h : CubicalEmbed.toCTerm a = CubicalEmbed.toCTerm b) : a = b :=
CubicalEmbed.toCTerm_injective h
-- ── §7. Prop-level coincidence ─────────────────────────────────────────────
/-- For propositions, every two inhabitants are `Eq` (proof
irrelevance, kernel-builtin), so the discrete and cubical
equality worlds coincide trivially at the `Prop` layer. -/
theorem Prop_eq_irrel {P : Prop} (a b : P) : a = b := rfl
end CubicalTransport.Bridge