Skip to content

Commit 3760d49

Browse files
examples: Example 10 — abstract interpretation (Sign lattice) (#82)
Adds `proofs/agda/EchoExampleAbsInt.agda` — Example 10 from the presentation-dependence cluster identified by PR #76. ## What lands - Sign lattice `{neg, zero, pos}` (no ⊥/⊤ — the headline is "many concretes ↦ one abstract", which the three-element lattice already exhibits; ⊥/⊤ is a Galois-closure refinement, separate story) - Hand-rolled five-element integer carrier `{m2, m1, z, p1, p2}` (avoids dragging `Data.Integer` into a small worked example) - Abstraction `α : Carrier → Sign` collapsing `{m1, m2} ↦ neg`, `{z} ↦ zero`, `{p1, p2} ↦ pos` - Headlines: `concretization-collapses`, `α-non-injective-on-pos`, `echo-pos-p1`, `echo-pos-p2`, `echo-zero-witness`, `distinct-echoes-same-sign`, `absint-classification` - Wired into `All.agda` (alphabetical) + `Smoke.agda` (per-lemma pins matching the suite's `using` convention) ## Cluster relationship Abstract interpretation is the canonical compiler-analysis lens on Echo: the abstraction is the structured loss, and Echo carries the concretes that collapse to a given abstract value. The *widening* / approximate-echo half of Example 10 (axis 2; ties to Example 6) is **not** addressed here — it lives in the `EchoApprox` tolerance-graded family. This PR is the exact-Galois- abstraction half only. `absint-classification` is the abstract-interpretation analogue of `EchoCharacteristic.collapse-classification` and the standard "preimage class is the partition cell" statement. ## Invariants - `--safe --without-K`; no postulates, no funext, no escape pragmas - Clean-worktree Agda build green: - `agda --safe --without-K EchoExampleAbsInt.agda` → exit 0 - `agda --safe --without-K All.agda` → exit 0 - `agda --safe --without-K Smoke.agda` → exit 0 (Agda 2.8.0 + stdlib v2; verified in `/tmp/echo-types-worktrees/ex10` on origin/main `4015df6`) - `EchoExamples.agda` NOT modified (other lanes also work on the examples cluster — new file is cleaner) Refs #76 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c064670 commit 3760d49

3 files changed

Lines changed: 192 additions & 1 deletion

File tree

proofs/agda/All.agda

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ open import AntiEcho
77
open import EchoKernel
88
open import EchoCharacteristic
99
open import EchoResidue
10+
open import EchoExampleAbsInt
11+
open import EchoExampleParser
1012
open import EchoExampleProvenance
1113
open import EchoExamples
12-
open import EchoExampleParser
1314

1415
open import EchoChoreo
1516
open import EchoEpistemic

proofs/agda/EchoExampleAbsInt.agda

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
{-# OPTIONS --safe --without-K #-}
2+
3+
-- EchoExampleAbsInt: Example 10 from `docs/echo-types/examples.md`
4+
-- (abstract interpretation via Sign lattice), realised as the
5+
-- canonical "abstraction map collapses many concretes to one
6+
-- abstract value" instance of Echo.
7+
--
8+
-- The carrier is a hand-rolled five-element integer toy
9+
-- `{m2, m1, z, p1, p2}` (i.e. -2, -1, 0, +1, +2). This keeps the
10+
-- example honest (a real `α : ℤ → Sign` would behave identically
11+
-- on each non-zero strict sign region) while avoiding the weight
12+
-- of `Data.Integer` for what is meant to be a small worked
13+
-- example in the examples cluster.
14+
--
15+
-- Abstraction: `α : Carrier → Sign` sends m1, m2 ↦ neg; z ↦ zero;
16+
-- p1, p2 ↦ pos. The non-injectivity of α on the `pos` (and `neg`)
17+
-- regions IS the lost concretion; `Echo α pos` is the carrier of
18+
-- the recovered concrete-class information.
19+
--
20+
-- Cluster relationship: presentation-dependence (PR #76). The
21+
-- abstract-interpretation example is the canonical compiler-analysis
22+
-- lens on Echo — the structured loss is the abstraction, and the
23+
-- intensional core distinguishes which concrete value in a Sign
24+
-- region was the actual program value. The "widening" / approximate
25+
-- variant (Example 6 + axis 2) is NOT addressed here; this module
26+
-- handles only the exact-Galois-abstraction half. Approximate
27+
-- widening would land in `EchoApprox`'s tolerance-graded family.
28+
--
29+
-- Headline lemmas (pinned in Smoke.agda):
30+
--
31+
-- * concretization-collapses -- Σ-witness of non-injectivity
32+
-- * α-non-injective-on-pos -- explicit p1 ≠ p2, α p1 ≡ α p2
33+
-- * echo-pos-p1, echo-pos-p2 -- two concretes for the pos region
34+
-- * echo-zero-witness -- the singleton echo at zero
35+
-- * distinct-echoes-same-sign -- the headline: two distinct
36+
-- Echo α pos witnesses share
37+
-- the same abstract value
38+
-- * absint-classification -- every Echo α pos has carrier
39+
-- p1 or p2 (i.e. the concrete
40+
-- class over pos is exactly
41+
-- the strictly-positive cell
42+
-- of the carrier)
43+
44+
module EchoExampleAbsInt where
45+
46+
open import Echo using (Echo; echo-intro)
47+
48+
open import Data.Product.Base using (Σ; _×_; _,_; proj₁)
49+
open import Data.Sum.Base using (_⊎_; inj₁; inj₂)
50+
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl; cong)
51+
52+
----------------------------------------------------------------------
53+
-- Sign lattice (no ⊥/⊤ — keep the abstract domain minimal; the
54+
-- ⊥/⊤ extension is the Galois-closure refinement and adds nothing
55+
-- to the "many concretes ↦ one abstract" headline).
56+
----------------------------------------------------------------------
57+
58+
data Sign : Set where
59+
neg : Sign
60+
zero : Sign
61+
pos : Sign
62+
63+
----------------------------------------------------------------------
64+
-- Hand-rolled five-element integer carrier. Standing for ℤ but
65+
-- finite, decidable-equality-free (no _≟_ needed for this example),
66+
-- and `--safe --without-K` friendly out of the box.
67+
----------------------------------------------------------------------
68+
69+
data Carrier : Set where
70+
m2 : Carrier -- -2
71+
m1 : Carrier -- -1
72+
z : Carrier -- 0
73+
p1 : Carrier -- +1
74+
p2 : Carrier -- +2
75+
76+
----------------------------------------------------------------------
77+
-- The abstraction. Galois-style: collapse the strict-sign regions
78+
-- to their representative element of the Sign lattice.
79+
----------------------------------------------------------------------
80+
81+
α : Carrier Sign
82+
α m2 = neg
83+
α m1 = neg
84+
α z = zero
85+
α p1 = pos
86+
α p2 = pos
87+
88+
----------------------------------------------------------------------
89+
-- Constructor disjointness (definitional; no postulates).
90+
----------------------------------------------------------------------
91+
92+
p1≢p2 : p1 ≢ p2
93+
p1≢p2 ()
94+
95+
m1≢m2 : m1 ≢ m2
96+
m1≢m2 ()
97+
98+
----------------------------------------------------------------------
99+
-- Headline 1 — `concretization-collapses`.
100+
--
101+
-- The Σ-witness of non-injectivity: two distinct concretes that
102+
-- α identifies. This is the bare information-loss statement, the
103+
-- "shadow collapse" half of the example.
104+
----------------------------------------------------------------------
105+
106+
concretization-collapses :
107+
Σ Carrier (λ x₁ Σ Carrier (λ x₂ x₁ ≢ x₂ × α x₁ ≡ α x₂))
108+
concretization-collapses = p1 , p2 , p1≢p2 , refl
109+
110+
----------------------------------------------------------------------
111+
-- Headline 2 — `α-non-injective-on-pos`.
112+
--
113+
-- The explicit named variant of the above, pinned to the `pos`
114+
-- region. Used downstream to keep proofs about the `pos` Echo
115+
-- carrier readable.
116+
----------------------------------------------------------------------
117+
118+
α-non-injective-on-pos : p1 ≢ p2 × α p1 ≡ α p2
119+
α-non-injective-on-pos = p1≢p2 , refl
120+
121+
----------------------------------------------------------------------
122+
-- Headline 3 — concrete echoes.
123+
--
124+
-- One per representative of the `pos` cell, plus the canonical
125+
-- `zero` witness. The `Echo α pos` carrier is `Σ Carrier (λ x → α x ≡ pos)`;
126+
-- both p1 and p2 inhabit it.
127+
----------------------------------------------------------------------
128+
129+
echo-pos-p1 : Echo α pos
130+
echo-pos-p1 = echo-intro α p1
131+
132+
echo-pos-p2 : Echo α pos
133+
echo-pos-p2 = echo-intro α p2
134+
135+
echo-zero-witness : Echo α zero
136+
echo-zero-witness = echo-intro α z
137+
138+
----------------------------------------------------------------------
139+
-- Headline 4 — `distinct-echoes-same-sign`.
140+
--
141+
-- The example's headline: two distinct echoes that share the same
142+
-- abstract value. The intensional core carries the concrete (p1 vs
143+
-- p2) while the extensional shadow has been quotiented to `pos`.
144+
----------------------------------------------------------------------
145+
146+
echo-pos-p1≢echo-pos-p2 : echo-pos-p1 ≢ echo-pos-p2
147+
echo-pos-p1≢echo-pos-p2 q = p1≢p2 (cong proj₁ q)
148+
149+
distinct-echoes-same-sign :
150+
Σ (Echo α pos) (λ e₁ Σ (Echo α pos) (λ e₂ e₁ ≢ e₂))
151+
distinct-echoes-same-sign = echo-pos-p1 , echo-pos-p2 , echo-pos-p1≢echo-pos-p2
152+
153+
----------------------------------------------------------------------
154+
-- Headline 5 — `absint-classification`.
155+
--
156+
-- The Sign-region classification: every `Echo α pos` has its
157+
-- carrier-projection inhabiting the strictly-positive cell `{p1, p2}`.
158+
-- This is the abstract-interpretation analogue of
159+
-- `EchoCharacteristic.collapse-classification` and the standard
160+
-- "preimage class is the cell of the partition" statement.
161+
--
162+
-- The proof case-splits on the carrier; the impossible cases
163+
-- (m2, m1, z) are ruled out by the proof `α x ≡ pos` reducing to
164+
-- a clash of distinct Sign constructors (Agda's empty-clause `()`).
165+
----------------------------------------------------------------------
166+
167+
absint-classification :
168+
(e : Echo α pos) proj₁ e ≡ p1 ⊎ proj₁ e ≡ p2
169+
absint-classification (m2 , ())
170+
absint-classification (m1 , ())
171+
absint-classification (z , ())
172+
absint-classification (p1 , _) = inj₁ refl
173+
absint-classification (p2 , _) = inj₂ refl

proofs/agda/Smoke.agda

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ open import EchoExampleParser using
9292
; paren-pair-balanced
9393
)
9494

95+
-- Example 10 from `docs/echo-types/examples.md` (abstract
96+
-- interpretation via Sign lattice). Headlines pinned so a rename
97+
-- or a slide back to an unanchored claim fails CI fast. See
98+
-- PR #76 (presentation-dependence cluster).
99+
open import EchoExampleAbsInt using
100+
( Sign
101+
; Carrier
102+
; α
103+
; concretization-collapses
104+
; α-non-injective-on-pos
105+
; echo-pos-p1
106+
; echo-pos-p2
107+
; echo-zero-witness
108+
; distinct-echoes-same-sign
109+
; absint-classification
110+
)
111+
95112
-- Example 5 (docs/echo-types/examples.md §5): database provenance via
96113
-- K-provenance semiring. Distinct Bool-provenance rows project to the
97114
-- same payload, witnessing the non-injectivity of `project` and

0 commit comments

Comments
 (0)