Skip to content

Commit f005a74

Browse files
hyperpolymathhyperpolymathclaude
authored
feat(formal): P-4 dynamic half — β-reduction preserves the QTT usage profile (#637)
## What Completes the **static/dynamic pair for P-4** (QTT affine usage). `QttTyping.v` (Wave 3) proved the *static* side — `usage_soundness`: the typing context records exactly the usage. This adds **`formal/QttDynamic.v`**, the *dynamic* side: a small-step β-reduction on the QTT term language (`AffineUsage.tm`) that carries the whole usage profile forward unchanged. ### Headline ```coq Theorem step_preserves_usage : forall t t', step t t' -> forall z, usage z t' = usage z t. ``` The quantity accounting is a **dynamic invariant**. Composed with the Wave-3 static capstone it yields: ```coq Corollary qtt_context_preserved : forall G t A t', qtt G t A -> step t t' -> forall x, usage x t' = G x. ``` — a well-typed term's QTT context stays valid across a reduction step (static + dynamic ⇒ the quantity discipline is sound under evaluation). ### Also - **QTT scaling law** `usage z (subst x v b) = usage z b + usage x b · usage z v` on the binder-free fragment, via a new **`qof_mult`** ×-homomorphism (`qof (m*n) = qmult (qof m) (qof n)`) — the analogue of `QttSemiring.qof_plus`. This is the multiplicity scaling that makes substructurality bite. - **`affine_no_increase`** — an affine redex (`usage x b ≤ 1`) never increases any variable's usage: the dynamic face of the affine bound. - **`linear_exact`** — a linear redex preserves usage exactly. β substitutes a **closed** value (the call-by-value, closed-program convention `P2_Stlc.v` also uses), so capture cannot arise and the development is **axiom-free** — all three `Print Assumptions` report *Closed under the global context*. ## Honest scope This is the dynamic half on the **same small QTT model** as Wave 2/3, not the full language. The remaining lift is the quantity-preserving substitution lemma generalised past the binder-free / closed-value fragment onto the real AST (tracked in `PROOF-NEEDS.adoc` §6 Wave 3 — the "real lift"). ## Wiring / docs - `formal/_CoqProject`, `formal/justfile` (dep-order compile + axiom gate), `.hypatia-ignore` (`.v` is Coq, not V-lang — both rule names), `formal/README.adoc` (Contents row + Wave-2/3 prose). - `docs/PROOF-NEEDS.adoc`: P-4 row + §6 Wave 2 updated (dynamic half no longer pending). `formal/` is now **14 files, 23 axiom-free closure reports**. Verified locally: all 14 proofs recompile in dependency order with `coqc -Q . ASFormal`; no `Axioms:` in any `Print Assumptions`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr --- _Generated by [Claude Code](https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr)_ Co-authored-by: hyperpolymath <paraordinate@yahoo.co.uk> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 55ae8bb commit f005a74

6 files changed

Lines changed: 237 additions & 15 deletions

File tree

.hypatia-ignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,5 @@ cicd_rules/vlang_detected:formal/AffineUsage.v
6969
cicd_rules/banned_language_file:formal/AffineUsage.v
7070
cicd_rules/vlang_detected:formal/QttTyping.v
7171
cicd_rules/banned_language_file:formal/QttTyping.v
72+
cicd_rules/vlang_detected:formal/QttDynamic.v
73+
cicd_rules/banned_language_file:formal/QttDynamic.v

docs/PROOF-NEEDS.adoc

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ than the prose corpus suggests:
7070
`TypeSafety.v` is example lemmas about list length, not AffineScript. Not
7171
core-metatheory.
7272
* **`formal/`** — the directory #513 names as the mechanized-proof target now
73-
**exists** (Coq/Rocq 8.18), axiom-free throughout — **13 files, 22 `Print
73+
**exists** (Coq/Rocq 8.18), axiom-free throughout — **14 files, 23 `Print
7474
Assumptions` closure reports**, all "Closed under the global context". Codegen
7575
keystone: `K1_CodegenPreservation.v` (K-1 minimal) +
7676
`K1Let_CodegenPreservation.v` (K-1 grown with variables/`let`/environment).
@@ -81,7 +81,8 @@ than the prose corpus suggests:
8181
`P3_BorrowGraph.v` (multi-resource loan graph) / `F3_PragmaDecidable.v` /
8282
`F4_ErrorFaithful.v`. Affine/QTT layer: `QttSemiring.v` (`{0,1,ω}`) +
8383
`AffineUsage.v` (`λx.x` affine, `λx. x x` not) + `QttTyping.v` (quantitative
84-
typing, `usage x t = Γ x` sound). See <<outstanding>>, <<faces>>.
84+
typing, `usage x t = Γ x` sound) + `QttDynamic.v` (the dynamic half:
85+
β-reduction preserves the usage profile). See <<outstanding>>, <<faces>>.
8586
* **Research tracks** (not core soundness): `docs/academic/tropical-session-types/`
8687
(Lean), `proposals/echo-types/EchoEncodingFaithfulness.agda`,
8788
`proposals/idaptik/migrated/**/*Boundary.agda` (echo-types loss-with-residue,
@@ -151,12 +152,15 @@ obligations. They are the "we might have missed" half of the brief.
151152
| **QTT affine usage.** Quantities `{0,1,ω}` are respected: `1`-vars used exactly
152153
once, `0`-vars erased, semiring laws hold.
153154
| L | `partial`
154-
| #513 must-have 3; **mechanized** across three files: `formal/QttSemiring.v`
155+
| #513 must-have 3; **mechanized** across four files: `formal/QttSemiring.v`
155156
(`{0,1,ω}` laws + occurrence homomorphism), `formal/AffineUsage.v` (`λx.x`
156-
affine, `λx. x x` not), and `formal/QttTyping.v` — a quantitative type system
157-
whose usage context Γ is proven to track usage exactly
158-
(`usage x t = Γ x`). The *dynamic* half (operational progress+preservation
159-
preserving the quantity accounting under reduction) remains; prose
157+
affine, `λx. x x` not), `formal/QttTyping.v` — a quantitative type system whose
158+
usage context Γ is proven to track usage exactly (`usage x t = Γ x`) — and
159+
`formal/QttDynamic.v`, the *dynamic* half: β-reduction **preserves the usage
160+
profile** (`t → t' ⇒ ∀z, usage z t' = usage z t`), with the QTT scaling law
161+
(`qof_mult`) and `qtt_context_preserved` composing it with `usage_soundness`.
162+
The full operational development on the *real* AST (substitution lemma past the
163+
binder-free / closed-value fragment) is the remaining lift; prose
160164
`quantitative-types.md`
161165

162166
| P-5
@@ -368,11 +372,13 @@ F-1 (full transformer preservation) is non-trivial rather than a formality.
368372
| Wave 0
369373

370374
| 2
371-
| **Done (static).** The affine/QTT layer — **`QttSemiring.v`** (`{0,1,ω}` laws +
375+
| **Done.** The affine/QTT layer — **`QttSemiring.v`** (`{0,1,ω}` laws +
372376
occurrence homomorphism), **`AffineUsage.v`** (`λx.x` affine, `λx. x x` not),
373-
**`QttTyping.v`** (quantitative typing, `usage x t = Γ x` sound). *Leftover:*
374-
the *dynamic* half — a quantitative substitution lemma over `P2_Stlc.v` that
375-
carries the quantity accounting through reduction.
377+
**`QttTyping.v`** (static quantitative typing, `usage x t = Γ x` sound), and
378+
**`QttDynamic.v`** — the *dynamic* half: β-reduction preserves the usage
379+
profile (`step_preserves_usage`), the QTT scaling law, and `qtt_context_preserved`
380+
tying it back to `usage_soundness`. *Leftover:* generalise the dynamic
381+
development past the binder-free / closed-value fragment onto the real AST.
376382
| Wave 1
377383

378384
| 3

formal/QttDynamic.v

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
(* SPDX-License-Identifier: MPL-2.0 *)
2+
(* SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) *)
3+
4+
(*
5+
QttDynamic.v
6+
════════════
7+
Wave 3, the DYNAMIC half of P-4. QttTyping.v proved the *static* quantity
8+
discipline sound — the typing context records exactly the usage
9+
(`usage x t = Γ x`). This file proves that discipline is preserved **under
10+
reduction**: a small-step β-reduction relation on the QTT term language
11+
(`AffineUsage.tm`) carries the whole usage profile forward unchanged.
12+
13+
Headline — `step_preserves_usage`:
14+
15+
t → t' → ∀ z, usage z t' = usage z t
16+
17+
so the quantity accounting is a dynamic invariant. Composed with QttTyping's
18+
`usage_soundness` it yields the capstone-completing corollary
19+
`qtt_context_preserved`:
20+
21+
qtt Γ t A → t → t' → ∀ x, usage x t' = Γ x
22+
23+
— a well-typed term's QTT context stays valid across a reduction step.
24+
25+
Two further results expose the QTT *scaling* law that makes substructurality
26+
bite. Substituting v for a variable used at multiplicity q scales v's usage by
27+
q (`usage_subst_scaling`, via the new ×-homomorphism `qof_mult`); and an
28+
affine redex never increases any variable's usage (`affine_no_increase`) — the
29+
dynamic face of the affine bound. β here substitutes a *closed* value (the
30+
call-by-value, closed-program convention P2_Stlc.v also uses), so capture
31+
cannot arise and the development stays axiom-free — no Admitted, no axioms.
32+
33+
`.v` is Coq, not V-lang — see formal/README.adoc and .hypatia-ignore.
34+
*)
35+
36+
Require Import PeanoNat.
37+
Require Import Lia.
38+
Require Import ASFormal.QttSemiring.
39+
Require Import ASFormal.AffineUsage.
40+
Require Import ASFormal.QttTyping.
41+
42+
(* ── qof is also a homomorphism for multiplication ──────────────────────────
43+
QttSemiring gives qof_plus (occurrences add); the scaling law below needs the
44+
× analogue (occurrences multiply through a binder of a given multiplicity). *)
45+
Lemma qof_mult : forall m n, qof (m * n) = qmult (qof m) (qof n).
46+
Proof.
47+
intros m n; destruct m as [|[|m]]; destruct n as [|[|n]];
48+
try reflexivity; rewrite Nat.mul_0_r; reflexivity.
49+
Qed.
50+
51+
(* ── capture-permitting substitution (only ever applied to closed values) ─── *)
52+
Fixpoint subst (x : id) (s t : tm) : tm :=
53+
match t with
54+
| var y => if Nat.eqb x y then s else var y
55+
| app f a => app (subst x s f) (subst x s a)
56+
| lam y b => if Nat.eqb x y then lam y b else lam y (subst x s b)
57+
end.
58+
59+
Definition closed (t : tm) : Prop := forall z, count z t = 0.
60+
61+
(* ── small-step β-reduction (call-by-value, closed argument) ──────────────── *)
62+
Inductive value : tm -> Prop :=
63+
| v_lam : forall x b, value (lam x b).
64+
65+
Inductive step : tm -> tm -> Prop :=
66+
| ST_AppAbs : forall x b v,
67+
value v -> closed v -> step (app (lam x b) v) (subst x v b)
68+
| ST_App1 : forall f f' a, step f f' -> step (app f a) (app f' a)
69+
| ST_App2 : forall f a a', value f -> step a a' -> step (app f a) (app f a').
70+
71+
(* ── substituting a closed value: counts untouched off x, zeroed at x ─────── *)
72+
73+
Lemma count_subst_closed_diff : forall x v b z,
74+
closed v -> z <> x -> count z (subst x v b) = count z b.
75+
Proof.
76+
intros x v b z Hc Hzx.
77+
induction b as [ y | f IHf a IHa | y b IHb ].
78+
- (* var y *) cbn [subst]. destruct (Nat.eqb x y) eqn:E.
79+
+ rewrite (Hc z). apply Nat.eqb_eq in E; subst y.
80+
cbn. apply Nat.eqb_neq in Hzx. rewrite Hzx. reflexivity.
81+
+ reflexivity.
82+
- (* app *) simpl. rewrite IHf, IHa. reflexivity.
83+
- (* lam y b *) cbn [subst]. destruct (Nat.eqb x y) eqn:E.
84+
+ reflexivity.
85+
+ simpl. destruct (Nat.eqb z y); [ reflexivity | exact IHb ].
86+
Qed.
87+
88+
Lemma count_subst_closed_same : forall x v b,
89+
closed v -> count x (subst x v b) = 0.
90+
Proof.
91+
intros x v b Hc.
92+
induction b as [ y | f IHf a IHa | y b IHb ].
93+
- cbn [subst]. destruct (Nat.eqb x y) eqn:E.
94+
+ apply (Hc x).
95+
+ cbn. rewrite E. reflexivity.
96+
- simpl. rewrite IHf, IHa. reflexivity.
97+
- cbn [subst]. destruct (Nat.eqb x y) eqn:E.
98+
+ cbn. rewrite E. reflexivity.
99+
+ cbn. rewrite E. exact IHb.
100+
Qed.
101+
102+
(* ── headline: reduction preserves the full usage profile ─────────────────── *)
103+
104+
Theorem step_preserves_usage : forall t t',
105+
step t t' -> forall z, usage z t' = usage z t.
106+
Proof.
107+
intros t t' Hs; induction Hs as
108+
[ x b v Hval Hcl | f f' a Hs IH | f a a' Hval Hs IH ]; intro z.
109+
- (* ST_AppAbs *)
110+
rewrite usage_app.
111+
assert (Hzv : usage z v = Zero) by (unfold usage; rewrite Hcl; reflexivity).
112+
rewrite Hzv, qplus_0_r.
113+
destruct (Nat.eqb z x) eqn:E.
114+
+ apply Nat.eqb_eq in E; subst z.
115+
rewrite usage_lam_bound.
116+
unfold usage; rewrite (count_subst_closed_same x v b Hcl); reflexivity.
117+
+ assert (Hzx : z <> x) by (apply Nat.eqb_neq; exact E).
118+
unfold usage. rewrite (count_subst_closed_diff x v b z Hcl Hzx).
119+
simpl. rewrite E. reflexivity.
120+
- (* ST_App1 *) rewrite !usage_app, (IH z). reflexivity.
121+
- (* ST_App2 *) rewrite !usage_app, (IH z). reflexivity.
122+
Qed.
123+
124+
(* ── the QTT scaling law (binder-free fragment, open v) ────────────────────
125+
Substituting v for x scales v's usage by x's multiplicity:
126+
usage z (subst x v b) = usage z b + (usage x b · usage z v).
127+
Stated on the lam-free fragment so capture cannot arise even for open v —
128+
enough to exhibit the qmult scaling that the closed-value headline hides. *)
129+
130+
Fixpoint lam_free (t : tm) : Prop :=
131+
match t with
132+
| var _ => True
133+
| app f a => lam_free f /\ lam_free a
134+
| lam _ _ => False
135+
end.
136+
137+
Lemma usage_subst_scaling : forall x v b z,
138+
z <> x -> lam_free b ->
139+
usage z (subst x v b) = qplus (usage z b) (qmult (usage x b) (usage z v)).
140+
Proof.
141+
intros x v b; induction b as [ y | f IHf a IHa | y b IHb ];
142+
intros z Hzx Hlf.
143+
- (* var y *) cbn [subst]. destruct (Nat.eqb x y) eqn:E.
144+
+ apply Nat.eqb_eq in E; subst y.
145+
rewrite (usage_var_diff z x Hzx), (usage_var_same x), qmult_1_l, qplus_0_l.
146+
reflexivity.
147+
+ assert (Hxy : x <> y) by (apply Nat.eqb_neq; exact E).
148+
rewrite (usage_var_diff x y Hxy), qmult_0_l, qplus_0_r. reflexivity.
149+
- (* app f a *) destruct Hlf as [Hlf Hla].
150+
cbn [subst].
151+
rewrite (usage_app z (subst x v f) (subst x v a)).
152+
rewrite (IHf z Hzx Hlf), (IHa z Hzx Hla).
153+
rewrite (usage_app z f a), (usage_app x f a).
154+
destruct (usage z f), (usage x f), (usage z a), (usage x a), (usage z v);
155+
reflexivity.
156+
- (* lam *) cbn in Hlf; destruct Hlf.
157+
Qed.
158+
159+
(* An affine redex (bound variable used ≤ 1×) never increases any variable's
160+
usage: the dynamic counterpart of the static affine bound. *)
161+
Corollary affine_no_increase : forall x v b z,
162+
z <> x -> lam_free b -> qle (usage x b) One ->
163+
qle (usage z (subst x v b)) (qplus (usage z b) (usage z v)).
164+
Proof.
165+
intros x v b z Hzx Hlf Haff.
166+
rewrite usage_subst_scaling by assumption.
167+
revert Haff; unfold qle.
168+
destruct (usage x b), (usage z b), (usage z v); simpl; lia.
169+
Qed.
170+
171+
(* A *linear* redex (bound variable used exactly once) preserves usage exactly,
172+
matching the closed-value headline but now for open v. *)
173+
Corollary linear_exact : forall x v b z,
174+
z <> x -> lam_free b -> usage x b = One ->
175+
usage z (subst x v b) = qplus (usage z b) (usage z v).
176+
Proof.
177+
intros x v b z Hzx Hlf Hlin.
178+
rewrite usage_subst_scaling by assumption.
179+
rewrite Hlin, qmult_1_l. reflexivity.
180+
Qed.
181+
182+
(* ── tie back to the Wave-3 static capstone ───────────────────────────────── *)
183+
184+
(* A well-typed term's QTT context stays valid across a reduction step:
185+
static usage_soundness (qtt Γ t A → usage x t = Γ x) composed with the
186+
dynamic invariant. Static + dynamic ⇒ the quantity discipline is sound. *)
187+
Corollary qtt_context_preserved : forall G t A t',
188+
qtt G t A -> step t t' -> forall x, usage x t' = G x.
189+
Proof.
190+
intros G t A t' HT Hs x.
191+
rewrite (step_preserves_usage t t' Hs x).
192+
exact (usage_soundness G t A HT x).
193+
Qed.
194+
195+
Print Assumptions step_preserves_usage.
196+
Print Assumptions usage_subst_scaling.
197+
Print Assumptions qtt_context_preserved.

formal/README.adoc

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ cannot mistake it. Coq has no `v.mod` manifest, so `vmod_detected` never fires.
8484
| **P-4** (Wave 3) — quantitative typing fusing all three: the typing context
8585
tracks usage, proven sound (`usage x t = Γ x`)
8686
| **mechanized**, axiom-free
87+
88+
| `QttDynamic.v`
89+
| **P-4** (Wave 3) — the *dynamic* half: β-reduction preserves the usage profile;
90+
QTT scaling law; ties back to `usage_soundness`
91+
| **mechanized**, axiom-free
8792
|===
8893

8994
All mechanized theorems report *Closed under the global context* under `Print
@@ -152,9 +157,20 @@ The quantities `{0,1,ω}` and their algebra are what make AffineScript *affine*:
152157
against occurrence counts. Corollary: a term typed in a singleton context uses
153158
that variable exactly once.
154159

155-
The remaining step is the *dynamic* half: operational progress + preservation
156-
that PRESERVES the quantity accounting under reduction (a quantitative
157-
substitution lemma over `P2_Stlc.v`).
160+
* **`QttDynamic.v`** (Wave 3) — the *dynamic* half. A small-step β-reduction on
161+
the QTT terms with the headline `step_preserves_usage`: `t → t'` implies
162+
`usage z t' = usage z t` for every `z`, so the quantity accounting is a
163+
*dynamic invariant*. Adds the QTT *scaling* law
164+
`usage z (subst x v b) = usage z b + usage x b · usage z v` (via the new
165+
`qof_mult` ×-homomorphism), an affine-no-increase corollary, and
166+
`qtt_context_preserved`, which composes the dynamic invariant with
167+
`usage_soundness` so a well-typed term's QTT context stays valid across a
168+
step. β substitutes a closed value (the closed-program convention `P2_Stlc.v`
169+
also uses), so it is capture-/axiom-free.
170+
171+
The remaining step is the full operational development on the *real* AST: the
172+
quantity-preserving substitution lemma generalised past the binder-free fragment
173+
and the closed-value convention.
158174

159175
== K-1 and its growth
160176

formal/_CoqProject

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ P2_Stlc.v
1212
QttSemiring.v
1313
AffineUsage.v
1414
QttTyping.v
15+
QttDynamic.v

formal/justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ check:
1212
for f in K1_CodegenPreservation K1Let_CodegenPreservation Siblings_Stated \
1313
F1_TransformerPreservation F3_PragmaDecidable F4_ErrorFaithful \
1414
P3_BorrowSound P3_BorrowGraph P2_Progress P2_Stlc \
15-
QttSemiring AffineUsage QttTyping; do
15+
QttSemiring AffineUsage QttTyping QttDynamic; do
1616
echo "== coqc $f.v =="
1717
o="$(coqc -Q . ASFormal "$f.v")"
1818
printf '%s\n' "$o"

0 commit comments

Comments
 (0)