Some checks are pending
Lean Action CI / build (push) Waiting to run
Implements the cells-spec vision: a computation space that preserves auditability, correctness, interactivity. Phase 1 (Lean kernel + naga-IR Rust backend) is closed; foundation hypothesis stack (Selection H1+H2, Subobject H3, Trace H5, Obs.Ctx C2, Cubical.Trace) landed. Highlights: - Cubical-HoTT syntax + value/eval/readback in Lean - naga-IR pipeline (no GLSL string crosses FFI; 17/17 probes pass) - Honesty audit: every non-transport (sealed cells, vertex shader, Y-flip, presentation conventions) is documented as such - Polymorphic Trace α as free monoid; Cubical.Trace gives CTerm → Trace CTerm by structural fold (homomorphism = definition) - Selection as Huet zipper; Subobject as Boolean algebra over WCell - All theorems proven; the proof IS the implementation See STATUS.md for the resume guide. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
207 lines
8.6 KiB
Text
207 lines
8.6 KiB
Text
/-
|
||
Topolei.Cubical.Trace
|
||
=====================
|
||
The trace map at the cubical-syntax level.
|
||
|
||
## What this file does
|
||
|
||
Given a cubical term `t : CTerm`, `traceOf t : Trace CTerm` returns
|
||
the list of *all sub-terms encountered* in walking `t` (including
|
||
`t` itself). This is the **provenance fold**: every constructor
|
||
visited, every variable referenced, every face-conditional clause
|
||
walked.
|
||
|
||
Every rendered output produced from `t` traces back to `traceOf t`.
|
||
No instrumentation of the renderer is required — the trace is a
|
||
property of the *cubical structure*, computable in pure Lean before
|
||
the term ever leaves the host for the GPU. The Rust side renders
|
||
whatever single concrete shader Lean hands it; the trace was already
|
||
extracted upstream.
|
||
|
||
## Why this is the Euler-elegant move
|
||
|
||
Composition of cubical terms (via `comp`, `compN`, `glueIn`,
|
||
`unglue`) automatically composes their traces — this is forced by
|
||
`traceOf`'s structural recursion + `Trace`'s monoid structure. The
|
||
homomorphism theorems below say: for any constructor `C` with
|
||
sub-terms `s₁..sₙ`,
|
||
|
||
traceOf (C s₁ … sₙ) = single (C s₁ … sₙ) ∪ traceOf s₁ ∪ … ∪ traceOf sₙ
|
||
|
||
Every one is `rfl`. The homomorphism IS the definition. No
|
||
external machinery, no enumerated cases, just structural recursion
|
||
realised as a fold + Trace's free-monoid algebra.
|
||
|
||
## What this gets us, semantically
|
||
|
||
Per-pixel traces (the user's "different pixels carrying projections
|
||
of different fibers") become straightforward once we add a face-
|
||
pruning version `traceOfAt : DimAssignment → CTerm → Trace CTerm`
|
||
that, before recursing into `compN`'s clauses, evaluates each face
|
||
formula at the given assignment and skips clauses whose face is
|
||
inactive. That's a sibling function we can land later.
|
||
|
||
Coherence between fibers (the differential / sheaf / bundle
|
||
question) is then a *predicate over `Trace CTerm`*: "the traces at
|
||
adjacent pixels share a long prefix," "the traces vary smoothly
|
||
along the rendered path," etc. All landed via simple `Prop`s, no
|
||
new types.
|
||
|
||
## Why no namespace wrap on `traceOf`
|
||
|
||
`CTerm` lives at the root namespace (Syntax.lean has no
|
||
`namespace` declaration), so `CTerm.traceOf` must too — that's
|
||
what makes the dot notation `t.traceOf` resolve for any `t : CTerm`.
|
||
Theorems are in a namespace below; the function is at root.
|
||
-/
|
||
|
||
import Topolei.Cubical.Syntax
|
||
import Topolei.Trace
|
||
|
||
open Topolei.Trace
|
||
|
||
-- ── traceOf : structural fold over CTerm ──────────────────────────────────
|
||
|
||
-- The trace of a cubical term: itself, plus the union of traces of
|
||
-- its immediate sub-terms (recursively).
|
||
--
|
||
-- Mutual with `traceOf.clauses` to handle `compN`'s list of face-
|
||
-- conditional sub-terms — same pattern as `CTerm.substDim` /
|
||
-- `CTerm.substDim.clauses` in `Syntax.lean`.
|
||
mutual
|
||
/-- The trace of a cubical term: itself, plus the union of traces of
|
||
its immediate sub-terms (recursively). -/
|
||
def CTerm.traceOf : CTerm → Trace CTerm
|
||
| t@(.var _) => Trace.single t
|
||
| t@(.lam _ body) =>
|
||
(Trace.single t).union body.traceOf
|
||
| t@(.app f a) =>
|
||
(Trace.single t).union (f.traceOf.union a.traceOf)
|
||
| t@(.plam _ body) =>
|
||
(Trace.single t).union body.traceOf
|
||
| t@(.papp body _) =>
|
||
(Trace.single t).union body.traceOf
|
||
| t@(.transp _ _ _ body) =>
|
||
(Trace.single t).union body.traceOf
|
||
| t@(.comp _ _ _ u v) =>
|
||
(Trace.single t).union (u.traceOf.union v.traceOf)
|
||
| t@(.compN _ _ clauses v) =>
|
||
(Trace.single t).union ((CTerm.traceOf.clauses clauses).union v.traceOf)
|
||
| t@(.glueIn _ a b) =>
|
||
(Trace.single t).union (a.traceOf.union b.traceOf)
|
||
| t@(.unglue _ f g) =>
|
||
(Trace.single t).union (f.traceOf.union g.traceOf)
|
||
| t@(.pair a b) =>
|
||
(Trace.single t).union (a.traceOf.union b.traceOf)
|
||
| t@(.fst a) =>
|
||
(Trace.single t).union a.traceOf
|
||
| t@(.snd a) =>
|
||
(Trace.single t).union a.traceOf
|
||
|
||
/-- Walk a `compN`'s face-conditional clauses, unioning each
|
||
sub-term's trace. The face formulas themselves contribute
|
||
*no* trace items — they're metadata about *when* the
|
||
sub-term participates, not source items. A future
|
||
`traceOfAt` will use the formulas to prune; the
|
||
unrestricted `traceOf` records all clauses unconditionally. -/
|
||
def CTerm.traceOf.clauses : List (FaceFormula × CTerm) → Trace CTerm
|
||
| [] => Trace.empty
|
||
| (_, c) :: rest => c.traceOf.union (CTerm.traceOf.clauses rest)
|
||
end
|
||
|
||
-- ── Theorems live in a sub-namespace ──────────────────────────────────────
|
||
|
||
namespace Topolei.Cubical.Trace
|
||
|
||
-- ── Homomorphism theorems (the construction-language equations) ──────────
|
||
--
|
||
-- For each cubical constructor C with sub-terms s₁..sₙ:
|
||
-- traceOf (C s₁ … sₙ) = single (C s₁ … sₙ) ∪ traceOf s₁ ∪ … ∪ traceOf sₙ
|
||
--
|
||
-- Every one of these is `rfl` by the definition above. This is the
|
||
-- "Euler-elegant" core: the homomorphism IS the definition; we don't
|
||
-- need separate proofs. The theorems exist as named references for
|
||
-- downstream code, and as a stable contract that future refactors
|
||
-- of `traceOf` must preserve.
|
||
|
||
@[simp] theorem traceOf_var (x : String) :
|
||
(CTerm.var x).traceOf = Trace.single (CTerm.var x) := rfl
|
||
|
||
@[simp] theorem traceOf_lam (x : String) (body : CTerm) :
|
||
(CTerm.lam x body).traceOf =
|
||
(Trace.single (CTerm.lam x body)).union body.traceOf := rfl
|
||
|
||
@[simp] theorem traceOf_app (f a : CTerm) :
|
||
(CTerm.app f a).traceOf =
|
||
(Trace.single (CTerm.app f a)).union (f.traceOf.union a.traceOf) := rfl
|
||
|
||
@[simp] theorem traceOf_plam (i : DimVar) (body : CTerm) :
|
||
(CTerm.plam i body).traceOf =
|
||
(Trace.single (CTerm.plam i body)).union body.traceOf := rfl
|
||
|
||
@[simp] theorem traceOf_papp (body : CTerm) (r : DimExpr) :
|
||
(CTerm.papp body r).traceOf =
|
||
(Trace.single (CTerm.papp body r)).union body.traceOf := rfl
|
||
|
||
@[simp] theorem traceOf_transp (i : DimVar) (A : CType)
|
||
(φ : FaceFormula) (body : CTerm) :
|
||
(CTerm.transp i A φ body).traceOf =
|
||
(Trace.single (CTerm.transp i A φ body)).union body.traceOf := rfl
|
||
|
||
@[simp] theorem traceOf_comp (i : DimVar) (A : CType) (φ : FaceFormula)
|
||
(u v : CTerm) :
|
||
(CTerm.comp i A φ u v).traceOf =
|
||
(Trace.single (CTerm.comp i A φ u v)).union
|
||
(u.traceOf.union v.traceOf) := rfl
|
||
|
||
@[simp] theorem traceOf_glueIn (φ : FaceFormula) (a b : CTerm) :
|
||
(CTerm.glueIn φ a b).traceOf =
|
||
(Trace.single (CTerm.glueIn φ a b)).union (a.traceOf.union b.traceOf) :=
|
||
rfl
|
||
|
||
@[simp] theorem traceOf_unglue (φ : FaceFormula) (f g : CTerm) :
|
||
(CTerm.unglue φ f g).traceOf =
|
||
(Trace.single (CTerm.unglue φ f g)).union (f.traceOf.union g.traceOf) :=
|
||
rfl
|
||
|
||
@[simp] theorem traceOf_pair (a b : CTerm) :
|
||
(CTerm.pair a b).traceOf =
|
||
(Trace.single (CTerm.pair a b)).union (a.traceOf.union b.traceOf) := rfl
|
||
|
||
@[simp] theorem traceOf_fst (a : CTerm) :
|
||
(CTerm.fst a).traceOf =
|
||
(Trace.single (CTerm.fst a)).union a.traceOf := rfl
|
||
|
||
@[simp] theorem traceOf_snd (a : CTerm) :
|
||
(CTerm.snd a).traceOf =
|
||
(Trace.single (CTerm.snd a)).union a.traceOf := rfl
|
||
|
||
-- ── Length / non-emptiness ────────────────────────────────────────────────
|
||
--
|
||
-- A trivial but useful corollary: every term's trace is non-empty
|
||
-- (it always contains at least the term itself). The user's
|
||
-- introspection guarantee depends on this — "every rendered element
|
||
-- has *some* provenance" is a typed property, not a runtime hope.
|
||
|
||
theorem traceOf_nonempty (t : CTerm) : t.traceOf.items ≠ [] := by
|
||
cases t <;> simp [CTerm.traceOf, Trace.single, Trace.union]
|
||
|
||
end Topolei.Cubical.Trace
|
||
|
||
-- ── Operational sanity ────────────────────────────────────────────────────
|
||
|
||
/-- A demo: the trace of `λx. x` (an identity term) contains the lam
|
||
AND the var. -/
|
||
def demoIdentity : CTerm := .lam "x" (.var "x")
|
||
|
||
#eval demoIdentity.traceOf.items.length -- expected: 2 (lam + var)
|
||
|
||
/-- A demo: the trace of `(a, b)` contains pair, var "a", var "b" → 3. -/
|
||
def demoPair : CTerm := .pair (.var "a") (.var "b")
|
||
|
||
#eval demoPair.traceOf.items.length -- expected: 3
|
||
|
||
/-- A demo: the trace of an application contains app + f-trace + a-trace. -/
|
||
def demoApp : CTerm := .app (.var "f") (.var "a")
|
||
|
||
#eval demoApp.traceOf.items.length -- expected: 3
|