import Semantics.Bind import Semantics.FixedPoint namespace Semantics.TopologicalPersistence open Semantics.Q16_16 open Semantics.Q16_16 -- ========================================================================= -- 1. Persistence Interval -- ========================================================================= /-- A persistence interval (birth, death) in Q0_16 normalized units. birth < death for valid intervals. In neural compression: birth = threshold when feature appears, death = threshold when feature disappears. -/ structure PersistentInterval where birth : Q0_16 death : Q0_16 deriving Repr, BEq, Inhabited /-- Persistence = death - birth. Longer = more robust feature. -/ def persistence (interval : PersistentInterval) : Q0_16 := Q0_16.sub interval.death interval.birth /-- A barcode is a list of persistence intervals. Represents the topological signature of a dataset. -/ def Barcode := List PersistentInterval deriving Repr, BEq, Inhabited -- ========================================================================= -- 2. Barcode Metrics -- ========================================================================= /-- Total persistence: sum of all interval lengths, promoted to Q16_16. Uses raw-value promotion (not semantic conversion) because this is an accumulator that may exceed Q0_16 range [-1, 1]. -/ def totalPersistence (barcode : Barcode) : Q16_16 := barcode.foldl (λ acc interval => let p := persistence interval Q16_16.add acc (Q16_16.ofNat p.val.toNat) ) (Q16_16.ofNat 0) /-- Count features with persistence above threshold. -/ def significantFeatures (barcode : Barcode) (threshold : Q0_16) : Nat := barcode.foldl (λ count interval => let p := persistence interval if p.val ≥ threshold.val then count + 1 else count ) 0 /-- Bottleneck distance: max difference between matched interval persistences. For #eval demo: pairs by position, computes max |p1 - p2|. Full TDA would use optimal matching (Hungarian); this is an upper bound. -/ def bottleneckDistance (b1 b2 : Barcode) : Q16_16 := let pairs := b1.zip b2 pairs.foldl (λ maxDiff pair => let (i1, i2) := pair let p1 := (persistence i1).val.toNat let p2 := (persistence i2).val.toNat let diff := if p1 ≥ p2 then p1 - p2 else p2 - p1 let diffQ := Q16_16.ofNat diff if diffQ.val > maxDiff.val then diffQ else maxDiff ) (Q16_16.ofNat 0) -- ========================================================================= -- 3. Topological Invariant Extractor (for bind) -- ========================================================================= /-- Canonical string invariant for geometricBind. Format: count=;total= -/ def barcodeInvariant (b : Barcode) : String := let count := b.length let total := totalPersistence b s!"count={count};total={total.val}" -- ========================================================================= -- 4. Cost Function -- ========================================================================= /-- Cost = bottleneck distance between original and compressed barcodes. Lower cost = more topologically similar. -/ def barcodeCost (original compressed : Barcode) (_metric : Metric) : Q16_16 := bottleneckDistance original compressed -- ========================================================================= -- 5. geometricBind Instance -- ========================================================================= /-- Bind two barcodes geometrically. Lawful iff their canonical invariants match exactly. Cost = bottleneck distance. -/ def topologicalBind (original compressed : Barcode) (metric : Metric) : Bind Barcode Barcode := geometricBind original compressed metric barcodeCost barcodeInvariant barcodeInvariant -- ========================================================================= -- 6. Verification Theorems -- ========================================================================= /-- Theorem: A barcode bound to itself is lawful. Identity bind preserves exact invariants. -/ theorem topologicalBind_selfLawful (b : Barcode) (metric : Metric) : (topologicalBind b b metric).lawful = true := by simp [topologicalBind, geometricBind, bind, barcodeInvariant] /-- Theorem: A barcode bound to itself produces zero bottleneck cost. Proven by native_decide on a concrete barcode witness. -/ theorem barcodeDistance_selfZero (b : Barcode) (h : b = [ { birth := ⟨0x1000⟩, death := ⟨0x7000⟩ }, { birth := ⟨0x2000⟩, death := ⟨0x6000⟩ } ]) : bottleneckDistance b b = Q16_16.ofNat 0 := by rw [h] native_decide -- ========================================================================= -- 7. Witness Examples -- ========================================================================= /-- Sample barcode: 3 features with varying persistence. -/ def sampleBarcode1 : Barcode := [ { birth := ⟨0x1000⟩, death := ⟨0x7000⟩ }, -- persistence ≈ 0.75 (raw 0x6000) { birth := ⟨0x2000⟩, death := ⟨0x6000⟩ }, -- persistence ≈ 0.50 (raw 0x4000) { birth := ⟨0x3000⟩, death := ⟨0x5000⟩ } -- persistence ≈ 0.25 (raw 0x2000) ] /-- Sample barcode 2: same first feature, second feature merged (persistence reduced). -/ def sampleBarcode2 : Barcode := [ { birth := ⟨0x1000⟩, death := ⟨0x7000⟩ }, -- unchanged { birth := ⟨0x2500⟩, death := ⟨0x5500⟩ } -- persistence ≈ 0.19 (raw 0x3000) ] #eval totalPersistence sampleBarcode1 #eval significantFeatures sampleBarcode1 ⟨0x1000⟩ #eval bottleneckDistance sampleBarcode1 sampleBarcode2 #eval (topologicalBind sampleBarcode1 sampleBarcode2 Metric.euclidean).cost #eval (topologicalBind sampleBarcode1 sampleBarcode2 Metric.euclidean).lawful end Semantics.TopologicalPersistence