cubical-transport-hott-lean4/Topolei/Obs/Ctx.lean
Maximus Gorog c2e3ecb3e3
Some checks are pending
Lean Action CI / build (push) Waiting to run
Initial commit: topolei — cubical-transport HoTT in Lean 4 + Rust FFI
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>
2026-04-27 20:40:45 -06:00

198 lines
7.9 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.

/-
Topolei.Obs.Ctx
===============
C2 of the categories-from-the-interface stack:
**the observation category for peripherals.**
Observations are typed peripheral configurations — they describe
*what surface the rendering lands on*, not the rendering itself.
Window dimensions, pixel format, eventually GPU adapter / mouse
position / clock tick. None of these are transports in the
cubical sense (cells-spec §1.7 calls them "sealed cells whose
interior cannot be deformed from above"); but they do organise
themselves into a category, and that category is what every
transport in the cell calculus eventually has to live over.
## What this file proves
We define `Ctx` as a record (the typed peripheral) and `resize` /
`reformat` as the basic ops. We prove the *laws* that say these
ops compose like morphisms in a category:
- **Identity**: `c.resize c.width c.height = c`,
`c.reformat c.pixelFmt = c`
- **Idempotence on overwrite**:
`(c.resize w₁ h₁).resize w₂ h₂ = c.resize w₂ h₂`
(the second resize wins; the first is forgotten)
- **Commutativity** of independent ops:
`(c.resize w h).reformat fmt = (c.reformat fmt).resize w h`
These laws *make `Ctx` a category* — objects are values of `Ctx`,
morphisms are equivalence classes of resize/reformat sequences,
and the laws above quotient the free monoid down to the right
thing. We don't materialise the category as a Lean structure
here because the laws-as-`@[simp]`-rewrites are enough for the
reasoning we'll need; if H4 (horizontal lifts of transports
along observation arrows) needs the explicit category, we add it
then.
## What this file does NOT do
- It does not define **the fibration** `p : Cells → Obs` that
the cells-spec promises. That's C5, derived from C2 + C3.
Once `Ctx` is in place, we lift `compileEMLPath` to take a
`Ctx` explicitly instead of having `width`/`height`/format
floating as bare arguments.
- It does not define **arrows as a typed inductive** (the
`Arrow : Ctx → Ctx → Type` form I sketched earlier). That
would force us to either prove laws *up to a quotient* or
use a higher-inductive type — overhead that buys nothing
new at this layer. Instead, we use the *equational
presentation*: ops + laws. When H4 needs to talk about
"an arrow `f : c → c'`" abstractly, we'll add the typed
inductive then.
- It does not yet bridge to `Topolei.Selection.Selection`. A
`WiredSelection` would pair `(c : Ctx) (s : Selection)` with
a compatibility predicate; deferred until we know which
compatibility actually matters for rendering.
## Reference
Cells-spec §1.5 ("Rendering Context as a Cell"), §15.2
("Presheaf of Potential Cells").
-/
namespace Topolei.Obs
-- ── Pixel format ──────────────────────────────────────────────────────────
--
-- The "type" of a framebuffer. Sealed-cell — its IEEE / sRGB /
-- linear-RGB semantics are determined by the GPU's hardware spec,
-- not by our calculus. We only carry it as a typed tag so
-- rendering pipelines can refuse to bind into a context whose
-- format they don't support.
inductive PixelFormat where
/-- 32-bit float per channel; no display clamp. Used by the
`render_faithful` probe so CPU/GPU agreement isn't
dominated by quantisation. -/
| rgbaF32 : PixelFormat
/-- 8-bit per channel sRGB. Display surface; values clamp
to [0, 1] and gamma-encode. Standard for live windows. -/
| rgbaSrgb : PixelFormat
deriving Repr, DecidableEq, Inhabited
-- ── Observation context ───────────────────────────────────────────────────
/-- An observation context: the typed peripheral configuration
a render lands on. Objects of the observation category. -/
structure Ctx where
width : Nat
height : Nat
pixelFmt : PixelFormat
deriving Repr, DecidableEq, Inhabited
namespace Ctx
-- ── Operations (morphisms in the implicit category) ──────────────────────
/-- Resize the observation surface. Width × height update. -/
def resize (c : Ctx) (w h : Nat) : Ctx :=
{ c with width := w, height := h }
/-- Change the pixel format. -/
def reformat (c : Ctx) (fmt : PixelFormat) : Ctx :=
{ c with pixelFmt := fmt }
-- ── Identity laws: ops with the current value are no-ops ─────────────────
/-- Resizing a context to its current dimensions is the identity. -/
@[simp] theorem resize_self (c : Ctx) :
c.resize c.width c.height = c := by
rcases c with ⟨_, _, _⟩
rfl
/-- Reformatting a context to its current format is the identity. -/
@[simp] theorem reformat_self (c : Ctx) :
c.reformat c.pixelFmt = c := by
rcases c with ⟨_, _, _⟩
rfl
-- ── Idempotence on overwrite: the last value wins ────────────────────────
/-- Composed resizes collapse: only the outer dimensions matter. -/
@[simp] theorem resize_resize (c : Ctx) (w₁ h₁ w₂ h₂ : Nat) :
(c.resize w₁ h₁).resize w₂ h₂ = c.resize w₂ h₂ := by
rcases c with ⟨_, _, _⟩
rfl
/-- Composed reformats collapse: only the outer format matters. -/
@[simp] theorem reformat_reformat (c : Ctx) (f₁ f₂ : PixelFormat) :
(c.reformat f₁).reformat f₂ = c.reformat f₂ := by
rcases c with ⟨_, _, _⟩
rfl
-- ── Commutativity of independent ops ─────────────────────────────────────
/-- Resize and reformat commute — they touch independent fields, so
order doesn't matter. -/
@[simp] theorem resize_reformat (c : Ctx) (w h : Nat) (fmt : PixelFormat) :
(c.resize w h).reformat fmt = (c.reformat fmt).resize w h := by
rcases c with ⟨_, _, _⟩
rfl
/-- Symmetric form: reformat-then-resize = resize-then-reformat. -/
theorem reformat_resize (c : Ctx) (fmt : PixelFormat) (w h : Nat) :
(c.reformat fmt).resize w h = (c.resize w h).reformat fmt :=
(Ctx.resize_reformat c w h fmt).symm
-- ── Read-back of the field updates ───────────────────────────────────────
-- These are field-update lemmas that downstream proofs will lean on.
@[simp] theorem resize_width (c : Ctx) (w h : Nat) :
(c.resize w h).width = w := by
rcases c with ⟨_, _, _⟩
rfl
@[simp] theorem resize_height (c : Ctx) (w h : Nat) :
(c.resize w h).height = h := by
rcases c with ⟨_, _, _⟩
rfl
@[simp] theorem resize_pixelFmt (c : Ctx) (w h : Nat) :
(c.resize w h).pixelFmt = c.pixelFmt := by
rcases c with ⟨_, _, _⟩
rfl
@[simp] theorem reformat_width (c : Ctx) (fmt : PixelFormat) :
(c.reformat fmt).width = c.width := by
rcases c with ⟨_, _, _⟩
rfl
@[simp] theorem reformat_height (c : Ctx) (fmt : PixelFormat) :
(c.reformat fmt).height = c.height := by
rcases c with ⟨_, _, _⟩
rfl
@[simp] theorem reformat_pixelFmt (c : Ctx) (fmt : PixelFormat) :
(c.reformat fmt).pixelFmt = fmt := by
rcases c with ⟨_, _, _⟩
rfl
end Ctx
-- ── Concrete demo (operational sanity check via `#eval`) ──────────────────
/-- Default rendering context: 800×600, sRGB. -/
def defaultCtx : Ctx :=
{ width := 800, height := 600, pixelFmt := PixelFormat.rgbaSrgb }
#eval defaultCtx.width -- expected: 800
#eval defaultCtx.resize 1024 768 |>.width -- expected: 1024
#eval (defaultCtx.resize 1024 768).reformat PixelFormat.rgbaF32 |>.pixelFmt
-- expected: PixelFormat.rgbaF32
#eval ((defaultCtx.resize 100 100).resize 200 200).width -- expected: 200
end Topolei.Obs