/- Topolei.Subobject ================= H3 of the selection-foundation stack. ## What this file is A `Subobject` of `WCell` is a *characteristic function*: `σ : WCell → Bool`, identifying which cells are "in" the subobject. Subobjects form a **Boolean algebra** under ∩, ∪, ¬ with units ⊤, ⊥ — every law follows from `Bool`'s algebra by pointwise reasoning, so the file is mostly `funext + simp + Bool`. ## Why this layer H1+H2 gave us *focused* selections — one cell, with a path back to the root. H3 gives us *scoped* selections — a *family* of candidate cells, with a focus picked from inside. The Boolean algebra then lets us: - intersect two scopes (∩) — "things in both selections"; - union two scopes (∪) — "things in either"; - complement (¬) — "things outside this selection". These operations are what peripheral observations need in order to *combine* or *refine* the cells they're looking at, without descending to ad-hoc selection tools per peripheral type (the VFX problem the user named: "their transports are forgetful, so they have many selector tools"). Here, ONE abstraction (Subobject + Boolean algebra) covers all of it. ## What this file does NOT contain - The *focused-subobject* combination (Selection × Subobject). That goes in `Topolei.Selection` once H1+H2's focus type is extended with a scope field. - The *action* of cell-endomorphisms on Subobjects. That's `Subobject.preimage` below — actually, it IS in this file. The thing NOT here is the action on `Selection` (which requires the focused-subobject layer to be in place). - **Heyting / intuitionistic refinement** to `WCell → Type` for proof-relevant subobjects (where membership tracks *why* a cell is in scope). That's a future H7 — when H5 (the trace map) needs provenance, we lift `σ`'s codomain from `Bool` to `Type`. For now, classical Boolean is enough. ## Reference - Cells-spec §15.4 ("Lawvere-Tierney topology") — Subobjects are exactly the level-0 instance of the LT-topology hierarchy the cells-spec uses for accessibility / security. - Cells-spec §1.5 ("Rendering Context as a Cell") — peripheral observations as fiber selectors. -/ namespace Topolei.Subobject -- We don't import `Topolei.Selection` to keep this file independent; -- both `Subobject` and `Selection` are foundational, neither -- depends on the other. The bridge between them lives in a -- third file. private inductive WCell where | mk : String → List WCell → WCell namespace WCell def data : WCell → String | .mk d _ => d def children : WCell → List WCell | .mk _ c => c end WCell -- ── The Subobject type ──────────────────────────────────────────────────── /-- A subobject of `WCell`: a Boolean-valued characteristic function saying which cells are "in" the subobject. Equivalence between subobjects is pointwise (function-extensional). -/ structure Subobject where σ : WCell → Bool deriving Inhabited namespace Subobject /-- Two subobjects are equal iff their characteristic functions are pointwise equal. Function extensionality is `funext`. -/ @[ext] theorem ext {a b : Subobject} (h : ∀ c, a.σ c = b.σ c) : a = b := by cases a; cases b congr 1 funext c exact h c -- ── Constants: ⊤ (everywhere) and ⊥ (nowhere) ──────────────────────────── /-- The total subobject — every cell is in it. -/ def top : Subobject := { σ := fun _ => true } /-- The empty subobject — no cell is in it. -/ def bot : Subobject := { σ := fun _ => false } -- ── Pointwise operations ───────────────────────────────────────────────── /-- Intersection (AND of characteristic functions). -/ def inter (a b : Subobject) : Subobject := { σ := fun c => a.σ c && b.σ c } /-- Union (OR of characteristic functions). -/ def union (a b : Subobject) : Subobject := { σ := fun c => a.σ c || b.σ c } /-- Complement (NOT of characteristic function). -/ def compl (a : Subobject) : Subobject := { σ := fun c => !(a.σ c) } -- ── Boolean algebra laws (every one follows from `Bool` algebra) ────────── -- The pattern: `ext c; simp [Subobject.inter, Subobject.union, Subobject.compl, -- Subobject.top, Subobject.bot, Bool.]`. -- ── Commutativity ───────────────────────────────────────────────────────── @[simp] theorem inter_comm (a b : Subobject) : a.inter b = b.inter a := by ext c; simp [inter, Bool.and_comm] @[simp] theorem union_comm (a b : Subobject) : a.union b = b.union a := by ext c; simp [union, Bool.or_comm] -- ── Associativity ───────────────────────────────────────────────────────── theorem inter_assoc (a b c : Subobject) : (a.inter b).inter c = a.inter (b.inter c) := by ext x; simp [inter, Bool.and_assoc] theorem union_assoc (a b c : Subobject) : (a.union b).union c = a.union (b.union c) := by ext x; simp [union, Bool.or_assoc] -- ── Idempotence ─────────────────────────────────────────────────────────── @[simp] theorem inter_self (a : Subobject) : a.inter a = a := by ext c; simp [inter] @[simp] theorem union_self (a : Subobject) : a.union a = a := by ext c; simp [union] -- ── Identity laws (top is unit of ∩, bot is unit of ∪) ─────────────────── @[simp] theorem inter_top (a : Subobject) : a.inter top = a := by ext c; simp [inter, top] @[simp] theorem top_inter (a : Subobject) : top.inter a = a := by ext c; simp [inter, top] @[simp] theorem union_bot (a : Subobject) : a.union bot = a := by ext c; simp [union, bot] @[simp] theorem bot_union (a : Subobject) : bot.union a = a := by ext c; simp [union, bot] -- ── Annihilation (top is absorber of ∪, bot of ∩) ──────────────────────── @[simp] theorem inter_bot (a : Subobject) : a.inter bot = bot := by ext c; simp [inter, bot] @[simp] theorem bot_inter (a : Subobject) : bot.inter a = bot := by ext c; simp [inter, bot] @[simp] theorem union_top (a : Subobject) : a.union top = top := by ext c; simp [union, top] @[simp] theorem top_union (a : Subobject) : top.union a = top := by ext c; simp [union, top] -- ── Distributivity ─────────────────────────────────────────────────────── theorem inter_distrib_union (a b c : Subobject) : a.inter (b.union c) = (a.inter b).union (a.inter c) := by ext x; simp [inter, union, Bool.and_or_distrib_left] theorem union_distrib_inter (a b c : Subobject) : a.union (b.inter c) = (a.union b).inter (a.union c) := by ext x; simp [union, inter, Bool.or_and_distrib_left] -- ── Complement laws ────────────────────────────────────────────────────── @[simp] theorem compl_compl (a : Subobject) : a.compl.compl = a := by ext c; simp [compl] @[simp] theorem inter_compl_self (a : Subobject) : a.inter a.compl = bot := by ext c; simp [inter, compl, bot] @[simp] theorem union_compl_self (a : Subobject) : a.union a.compl = top := by ext c; simp [union, compl, top] @[simp] theorem compl_top : (top : Subobject).compl = bot := by ext c; simp [compl, top, bot] @[simp] theorem compl_bot : (bot : Subobject).compl = top := by ext c; simp [compl, top, bot] -- ── De Morgan ──────────────────────────────────────────────────────────── theorem compl_inter (a b : Subobject) : (a.inter b).compl = a.compl.union b.compl := by ext c; simp [inter, union, compl, Bool.not_and] theorem compl_union (a b : Subobject) : (a.union b).compl = a.compl.inter b.compl := by ext c; simp [union, inter, compl, Bool.not_or] -- ── Absorption ─────────────────────────────────────────────────────────── @[simp] theorem inter_union_self (a b : Subobject) : a.inter (a.union b) = a := by ext c; cases h : a.σ c <;> simp [inter, union, h] @[simp] theorem union_inter_self (a b : Subobject) : a.union (a.inter b) = a := by ext c; cases h : a.σ c <;> simp [inter, union, h] -- ── Bridge to construction: scope-preserving endomorphisms ─────────────── -- -- A peripheral observation produces a `Subobject` (the cells the -- user is "looking at"). A constructor — i.e. an endomorphism of -- WCell — should respect the scope: cells inside the selection -- map to cells inside the selection. This is the type-level -- guarantee that "applying a tool to a selection produces a new -- valid selection". -- -- Note: this is a *property* of an endomorphism, not a structure -- on Subobjects. The action on Selections — `applyEndo` — uses -- this property as a precondition; it lives in -- `Topolei.Selection.Scoped` (next module to land). /-- `f` preserves the subobject `a`: every cell in `a` maps to a cell in `a`. This is the scope-preservation precondition for actions on focused-subobject selections. -/ def Preserves (a : Subobject) (f : WCell → WCell) : Prop := ∀ c, a.σ c = true → a.σ (f c) = true /-- The identity always preserves any subobject. -/ theorem Preserves.id (a : Subobject) : Preserves a (fun c => c) := fun _ h => h /-- Composition of preserving endomorphisms is preserving. -/ theorem Preserves.comp {a : Subobject} {f g : WCell → WCell} (hf : Preserves a f) (hg : Preserves a g) : Preserves a (fun c => f (g c)) := fun c hc => hf (g c) (hg c hc) end Subobject -- ── Operational sanity check ────────────────────────────────────────────── /-- A demo subobject: cells whose data starts with `"leaf"`. -/ def demoLeaves : Subobject := { σ := fun c => c.data.startsWith "leaf" } #eval demoLeaves.σ (WCell.mk "leaf-A" []) -- expected: true #eval demoLeaves.σ (WCell.mk "root" []) -- expected: false #eval (demoLeaves.inter demoLeaves).σ (WCell.mk "leaf-A" []) -- expected: true #eval (demoLeaves.compl).σ (WCell.mk "root" []) -- expected: true #eval (Subobject.top : Subobject).σ (WCell.mk "anything" []) -- expected: true #eval (Subobject.bot : Subobject).σ (WCell.mk "anything" []) -- expected: false end Topolei.Subobject