Skip to content

Commit f067e18

Browse files
committed
feat(aggregation): mechanise micro→macro aggregation as Echo / no-section
Add EchoAggregation.agda: economic aggregation is an Echo map, and the non-identifiability of the micro state from the macro observable ("you cannot disaggregate") is the repo's no-section theorem. - aggregate : (ℕ × ℕ) → ℕ two-account ledger → Godley column total - ConsistentLedgers m = Echo aggregate m the fibre, definitionally - aggregate-non-injective the fibre is non-trivial (many-to-one) - no-canonical-disaggregation no section of aggregate, via no-section-of-collapsing-map Reuses the same no-section machinery that grounds the affine⊑linear wasm-typing story; the fibre identity is definitional, so downstream Echo/EchoResidue results apply to aggregation unchanged. Wired into All.agda; headlines pinned in Smoke.agda. Verifies under --safe --without-K with zero postulates (All.agda and Smoke.agda exit 0). Mechanises §2 of the oikos alib design note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VwbFNQJw23tW8tqM7utWku
1 parent 4e9e1c3 commit f067e18

3 files changed

Lines changed: 206 additions & 0 deletions

File tree

proofs/agda/All.agda

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ open import EchoOrthogonalFactorizationSystem
1010
open import EchoImageFactorization
1111
open import EchoImageFactorizationProp
1212
open import EchoNoSectionGeneric
13+
open import EchoAggregation
1314
open import EchoLossTaxonomy
1415
open import EchoResidueTaxonomy
1516
open import EchoDecorationStructure

proofs/agda/EchoAggregation.agda

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
{-# OPTIONS --safe --without-K #-}
2+
-- SPDX-License-Identifier: MPL-2.0
3+
-- SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
5+
-- EchoAggregation: micro→macro economic aggregation as structured loss.
6+
--
7+
-- This module mechanises the keystone claim of the oikos/betlang
8+
-- "aggregate library" design note (oikos
9+
-- `docs/alib-aggregate-bridge.adoc` §2): economic *aggregation* —
10+
-- rolling a micro ledger up into a macro observable — is literally an
11+
-- `Echo` map, and the *non-identifiability* of the micro state from
12+
-- the macro observable ("you cannot disaggregate") is literally the
13+
-- repo's `no-section` theorem.
14+
--
15+
-- The honest minimal instance. The alib's `MacroState` is a rich
16+
-- record (population, elites, capital stock, …). Each of its fields
17+
-- is an aggregation of the same shape: a sum (a Godley column) of
18+
-- micro entries. The load-bearing structural fact is visible already
19+
-- at the smallest faithful case — a two-account ledger collapsing to
20+
-- a total:
21+
--
22+
-- * `MicroLedger = ℕ × ℕ` two sector balances (e.g. household,
23+
-- firm) — the micro state;
24+
-- * `MacroTotal = ℕ` the aggregate money stock — one Godley
25+
-- column sum, the macro observable;
26+
-- * `aggregate (a , b) = a + b` the rollup.
27+
--
28+
-- The full `MacroState` is then a product of such projections; the
29+
-- structural story (many-to-one ⇒ no canonical disaggregation) is
30+
-- identical field-by-field, so the single-column instance is the
31+
-- right place to pin it.
32+
--
33+
-- What is proved.
34+
--
35+
-- * `ConsistentLedgers m = Echo aggregate m` — the fibre: ALL micro
36+
-- ledgers consistent with the macro total `m`. This IS the
37+
-- economist's "aggregation is many-to-one", as a type.
38+
-- * `aggregate-non-injective` — two distinct micro ledgers,
39+
-- `(0,1)` and `(1,0)`, are distinct echoes at the SAME macro
40+
-- total `1`. The fibre is genuinely non-trivial.
41+
-- * `no-canonical-disaggregation` (keystone) — `aggregate` admits
42+
-- NO section: there is no `raise : MacroTotal → MicroLedger`
43+
-- recovering the micro split from the macro total for every
44+
-- input. This is the aggregation / non-identifiability problem,
45+
-- as a theorem, obtained by instantiating the generic
46+
-- `EchoNoSectionGeneric.no-section-of-collapsing-map`.
47+
--
48+
-- This is the SAME `no-section` machinery that underwrites the
49+
-- affine⊑linear story in the wasm proof layer (`EchoLinear.weaken`,
50+
-- machine-checked equal to AffineScript subtyping in
51+
-- `nextgen-typing`'s `EchoTyping.agda`). One type language serves
52+
-- micro→macro aggregation, cross-language ABI, and uncertainty.
53+
--
54+
-- Headlines (pinned in Smoke.agda):
55+
--
56+
-- * aggregate -- the rollup map
57+
-- * ConsistentLedgers -- its fibre, as an Echo
58+
-- * aggregate-non-injective -- the fibre is non-trivial
59+
-- * no-canonical-disaggregation -- the keystone: no section
60+
--
61+
-- Scope guardrail. `aggregate` here is a concrete finite ℕ-valued
62+
-- map; the theorem is about THIS map's non-injectivity. It does NOT
63+
-- claim a quantitative bound on the size of fibres, nor anything
64+
-- about the rich `MacroState` record's joint identifiability — those
65+
-- are downstream, and named in the alib note's open questions. The
66+
-- minimal claim is exactly the load-bearing one: aggregation is an
67+
-- Echo, and the macro observable cannot in general be disaggregated.
68+
69+
module EchoAggregation where
70+
71+
open import Echo using (Echo; echo-intro)
72+
open import EchoNoSectionGeneric using (no-section-of-collapsing-map)
73+
74+
open import Data.Nat.Base using (ℕ; _+_)
75+
open import Data.Product.Base using (Σ; _×_; _,_; proj₁)
76+
open import Relation.Binary.PropositionalEquality
77+
using (_≡_; _≢_; refl; cong)
78+
open import Relation.Nullary using (¬_)
79+
80+
----------------------------------------------------------------------
81+
-- The micro / macro types and the aggregation map.
82+
----------------------------------------------------------------------
83+
84+
-- A micro ledger: two sector balances (e.g. household, firm).
85+
MicroLedger : Set
86+
MicroLedger = ℕ × ℕ
87+
88+
-- The macro observable: one aggregate total (a Godley column sum).
89+
MacroTotal : Set
90+
MacroTotal =
91+
92+
-- Aggregation: roll the micro ledger up into the macro total.
93+
aggregate : MicroLedger MacroTotal
94+
aggregate (a , b) = a + b
95+
96+
----------------------------------------------------------------------
97+
-- The fibre, as an Echo.
98+
--
99+
-- `ConsistentLedgers m` is the type of ALL micro ledgers whose rollup
100+
-- is exactly the macro total `m`. Definitionally it is
101+
-- `Σ MicroLedger (λ l → aggregate l ≡ m)` — the fibre of `aggregate`
102+
-- over `m`. "Aggregation is many-to-one" becomes "this type can have
103+
-- more than one inhabitant" (witnessed below).
104+
----------------------------------------------------------------------
105+
106+
ConsistentLedgers : MacroTotal Set
107+
ConsistentLedgers m = Echo aggregate m
108+
109+
----------------------------------------------------------------------
110+
-- The fibre over macro total 1 is non-trivial: two distinct micro
111+
-- ledgers, (0,1) and (1,0), both aggregate to 1.
112+
----------------------------------------------------------------------
113+
114+
ledger₁ : MicroLedger
115+
ledger₁ = 0 , 1
116+
117+
ledger₂ : MicroLedger
118+
ledger₂ = 1 , 0
119+
120+
-- The two micro ledgers are distinct: their household balances differ
121+
-- (0 vs 1). Refuted at the first projection by constructor clash.
122+
ledger₁≢ledger₂ : ledger₁ ≢ ledger₂
123+
ledger₁≢ledger₂ eq with cong proj₁ eq
124+
... | ()
125+
126+
-- … yet they collapse to the same macro total (both 1).
127+
aggregate-collapses : aggregate ledger₁ ≡ aggregate ledger₂
128+
aggregate-collapses = refl
129+
130+
-- As echoes at the same macro total.
131+
echo-ledger₁ : ConsistentLedgers 1
132+
echo-ledger₁ = echo-intro aggregate ledger₁
133+
134+
echo-ledger₂ : ConsistentLedgers 1
135+
echo-ledger₂ = echo-intro aggregate ledger₂
136+
137+
-- The fibre is genuinely non-trivial: two distinct inhabitants at the
138+
-- same macro observable. This is "aggregation is many-to-one", as a
139+
-- checked theorem.
140+
aggregate-non-injective : echo-ledger₁ ≢ echo-ledger₂
141+
aggregate-non-injective eq = ledger₁≢ledger₂ (cong proj₁ eq)
142+
143+
----------------------------------------------------------------------
144+
-- The keystone: no canonical disaggregation.
145+
--
146+
-- There is no section `raise : MacroTotal → MicroLedger` recovering
147+
-- the micro split from the macro total for every input — i.e. no
148+
-- function with `raise (aggregate l) ≡ l` for all micro ledgers `l`.
149+
-- This is the aggregation / non-identifiability problem of
150+
-- macroeconomics, obtained as a one-instance application of the
151+
-- generic no-section theorem.
152+
----------------------------------------------------------------------
153+
154+
no-canonical-disaggregation :
155+
¬ Σ (MacroTotal MicroLedger)
156+
(λ raise l raise (aggregate l) ≡ l)
157+
no-canonical-disaggregation =
158+
no-section-of-collapsing-map
159+
aggregate
160+
ledger₁ ledger₂
161+
ledger₁≢ledger₂
162+
aggregate-collapses
163+
164+
----------------------------------------------------------------------
165+
-- Companion remark.
166+
--
167+
-- Why this is the right level of generality:
168+
--
169+
-- * The fibre `ConsistentLedgers m` is `Echo aggregate m` ON THE
170+
-- NOSE (definitional), so every downstream `Echo`/`EchoResidue`
171+
-- result applies to aggregation without restatement. In
172+
-- particular the residue machinery names what the macro layer is
173+
-- entitled to observe after the loss.
174+
--
175+
-- * `no-canonical-disaggregation` refutes a LEFT inverse (a
176+
-- section of `aggregate`). It does NOT refute the existence of
177+
-- SOME right inverse / choice of representative — economists pick
178+
-- representatives all the time (a "typical household"). The
179+
-- content is precisely that no such choice is CANONICAL: it
180+
-- cannot satisfy `raise ∘ aggregate ≡ id`, so it always discards
181+
-- information about ledgers it did not pick.
182+
--
183+
-- * Promoting this to the rich `MacroState` record is mechanical:
184+
-- each field is an aggregation of this shape, and a section of
185+
-- the product would restrict to a section of each projection,
186+
-- which this theorem already refutes. No new proof idea is
187+
-- needed; see the alib note §3–§4.
188+
----------------------------------------------------------------------

proofs/agda/Smoke.agda

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ open import EchoNoSectionGeneric using
8787
; no-section-when-non-injective-at-y
8888
)
8989

90+
-- EchoAggregation — micro→macro economic aggregation as structured
91+
-- loss (the oikos/betlang "aggregate library" keystone). `aggregate`
92+
-- rolls a two-account ledger up into a Godley column total; its fibre
93+
-- `ConsistentLedgers m = Echo aggregate m` is the set of micro states
94+
-- consistent with the macro observable. `aggregate-non-injective`
95+
-- pins "aggregation is many-to-one"; `no-canonical-disaggregation`
96+
-- pins the non-identifiability theorem (no section of `aggregate`)
97+
-- via `no-section-of-collapsing-map`.
98+
open import EchoAggregation using
99+
( aggregate
100+
; ConsistentLedgers
101+
; ledger₁≢ledger₂
102+
; aggregate-collapses
103+
; aggregate-non-injective
104+
; no-canonical-disaggregation
105+
)
106+
90107
-- EchoImageFactorization — image-factorisation triangle in Echo
91108
-- language. `Image f := Σ B (Echo f)` IS the total Echo space.
92109
-- Three classifications (Surjective / Injective / projection-

0 commit comments

Comments
 (0)