pRHL, version 2 — the literal explicit-coupling judgment #
This module develops the candidate pRHL definition from CLAUDE.md
subtask 3 in its most literal form and shows it delivers the same rule
set as the existing ProgramDenotation.prhl (PRHL/Prhl.lean).
The difference from ProgramDenotation.prhl #
ProgramDenotation.prhl packages the coupling existential inside the ProgramDenotation.Coupling
structure, whose marginals are stated in expected-value form
(∀ F, μ.expected (F ∘ fst) = c.wp F σ₁) and whose support uses the
range form (∀ f vanishing on B, ∫ f dμ = 0).
ProgramDenotation.prhl2 instead spells out the existential directly, exactly as in
the subtask-3 text:
prhl2 A c d B := ∀ σ₁ σ₂, A σ₁ σ₂ →
∃ μ, map fst μ = c σ₁ ∧ map snd μ = d σ₂ ∧ satisfy μ B
with the marginals as distribution equality (map fst μ = c σ₁,
written μ >>= fun x => pure x.1 = c σ₁) and the support as the pointwise
SubProbability.satisfies (∀ x, μ {x} ≠ 0 → B x).
What this buys #
The two formulations are interderivable, so prhl2 inherits every rule:
ProgramDenotation.prhl.to_prhl2(forward) recovers the distribution-equality marginals viaCoupling.map_fst/map_snd, andsatisfies_of_rangeturns the range support into the pointwise one.ProgramDenotation.prhl2.to_prhl(backward) reads the pointwisesatisfiesback as the range form because the integral is the sum of its atoms (range_of_satisfies).
Originally the backward direction (and every rule consuming a coupling) carried a [Countable]
hypothesis: the atom-sum identity was lintegral_countable'. Since subtask 4 this is gone — the
SubProbability discreteness invariant gives the atom-sum (lintegral_eq_tsum_smul), the marginals
(discreteMeasure_measure_iUnion), and the sampling swap (lintegral_lintegral_swap_discrete) with
no countability of the carriers. So prhl2 and all its rules are now countability-free.
Universes #
As in Language/Footprint.lean: states live in Type (max u v) and results in Type v, here for
both sides of a judgment, so that a side α × s₁ and the joint (α × s₁) × (β × s₂) share one
universe and the marginals are plain >>=. A state in Type 1 with results in Type is
covered. The SubProbability helpers are stated in a single universe u; where a sampled value
is bound directly into the state (toProgram_bind_apply, swap_sample, rnd) the bind is
SubProbability.hbind. The rules that chain a Unit-valued program with the results
(prefix_*, cond, the lossless-tail rules) keep the results in Type.
The judgment #
Literal coupling-based pRHL: the subtask-3 existential, with
marginals as distribution equality and support as the pointwise
SubProbability.satisfies.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Pushforward of expected along a deterministic map: integrating F
against map g μ is integrating F ∘ g against μ. The workhorse for
manipulating the literal map-marginals.
Discrete disintegration toolkit (for trans) #
Over countable carriers (the ⊤ σ-algebra) a SubProbability is determined
by its atom weights, and the integral is a countable sum of atoms. These
helpers let us build the glued coupling for transitivity as an explicit
atomic measure and reason about its marginals by tsum algebra.
Integral as a sum of atoms. Countability-free (subtask 4): via the discreteness invariant
(lintegral_eq_tsum_smul) rather than lintegral_countable'.
An atomic sub-probability built from a summable weight function.
Equations
- GaudisCrypt.SubProbability.ofWeights w h = ⟨MeasureTheory.Measure.sum fun (t : T) => w t • MeasureTheory.Measure.dirac t, ⋯⟩
Instances For
A left-marginal atom is the sum of the joint atoms over the fiber. Countability-free
(subtask 4): discreteMeasure_measure_iUnion instead of measure_iUnion.
A right-marginal atom is the sum of the joint atoms over the fiber. Countability-free
(subtask 4): discreteMeasure_measure_iUnion instead of measure_iUnion.
The atom weights of a sub-probability sum to at most one. Countability-free (subtask 4):
∑' t, ν{t} = ν univ directly from the discreteness invariant at univ.
Bind algebra and fixed-point helpers (for while_loop) #
A lifted sampling unfolded at a state.
A lifted sampling threads the state unchanged: (sample; K) σ = μ.hbind K(·,σ) (hbind:
the state may live in a different universe from the sampled values).
Commutativity of independent sampling (Tonelli): two state-free samplings can be drawn in either order.
Swap two independent (state-free) samplings in a program.
A bind whose continuation reads only the first coordinate factors through the first marginal.
A bind whose continuation reads only the second coordinate factors through the second marginal.
Two binds with continuations agreeing on the support are equal.
The integral against a least fixed point is the supremum of the integrals against the Kleene iterates (monotone convergence).
If every Kleene iterate is supported in B, so is the least fixed point.
Support of a bind: if each fibre is supported in B, so is the bind.
satisfies_bind across universes.
Monotone convergence for the program while_loop (curried fixed point).
Unfold one step of the while_iteration functional at a state.
Bridges to ProgramDenotation.prhl #
Forward bridge (unconditional): the structure-packaged judgment
yields the literal one. Marginals come from Coupling.map_fst/map_snd;
the pointwise support comes from the range support via
satisfies_of_range.
Backward bridge (discrete): the literal judgment yields the
structure-packaged one. The marginal equalities become the
expected-value marginals by integrating both sides; the pointwise
support becomes the range support via range_of_satisfies (countability-free
since subtask 4, via the discreteness invariant).
For discrete (countable) joint type the two formulations coincide.
Soundness with respect to the wp-lifting #
Every literal coupling judgment yields the two-sided wp-lifting
judgment (discrete); all relE elimination forms transfer.
Structural rules #
The leaves and the purely structural rules are unconditional; the seq rule inherits the discreteness hypothesis (see the module header).
Consequence — same witness, the support and precondition only weaken.
Reflexivity.
The rnd rule: uniform samples coupled along a bijection.
Existential in the precondition.
Disjunction in the precondition.
The seq rule (discrete). The composite coupling is built by
ProgramDenotation.prhl.bind; the discreteness hypotheses are what let the
pointwise-satisfies prefixes and continuations be reassembled.
Symmetry: a coupling is inherently two-sided — swap the joint
distribution coordinate-wise. The pointwise satisfies is propagated through the swap map (via
the range form), countability-free since subtask 4.
Read coupling: two gets relate when the read values (and unchanged
states) satisfy the post. Unconditional leaf.
Write coupling: two sets relate when the updated states satisfy the
post. Unconditional leaf.
Bounded-loop congruence: if the bodies preserve the invariant Inv
relationally, so do their n-fold iterates. Proved by induction on n
using prhl2.bind; covers the loops the crypto clients actually use
(oracle_loop_n). The unbounded while fixed point stays open.
Left footprint: strengthen the post with a left-side fact C that
holds almost surely for c (i.e. fails with probability 0). This is how
inRange-style unary facts enter the coupling logic. Discrete (the
support is rebalanced).
Right footprint: the mirror of strengthen_left, obtained by
symmetry.
Tier 2: one-sided/frame rules and rnd generalizations #
Left frame: a left-only prefix p₀ matched against skip on the
right (carrying Pre to Mid), then the continuations from Mid,
gives (p₀; k) ~ q. Avoids inserting pure () >>= on the right by
hand. Derived from bind + the monad law.
Right frame: the mirror of prefix_left.
Left ghost write: a left-only set L v matched against skip. The
coupling analogue of EquivModuloLens.set_equiv_pure. Unconditional
(both sides are point masses).
Right ghost write: the mirror of set_skip_left.
Synchronized sampling (rnd with the identity coupling): both runs
draw the same uniform value. The common special case of uniform.
Tier 3: transitivity by discrete disintegration #
Transitivity: compose a coupling of (p, q) with a coupling of
(q, r) into a coupling of (p, r), gluing along the shared middle
marginal q σ₂. The glued weight is
ν{(x,z)} = ∑ₘ μ₁{(x,m)}·μ₂{(m,z)} / q{m} — the discrete disintegration
(independent given the middle), with the middle weights cancelling in
each marginal. Countability-free since subtask 4 (the discreteness invariant).
Synchronized while rule (the coupling least fixed point). Under the
invariant the guards are coupled to agree (PostC records the invariant
refined by the guard value), the bodies preserve the invariant from
PostC true, and the loops relate at PostC false. The witness coupling
is Φ.lfp, the least fixed point of the coupling transformer that runs
the guard coupling and, while it fires, the body coupling.
Synchronized conditional (if): if the guards are coupled to
produce equal booleans (carrying Mid), and the branches are related
from Mid, then the conditionals are related. (Binds its own s₁ s₂ : Type u: it mentions
neither α nor β, so the section's Type (max u v) would leave v free.)
Case split on a state predicate P.
General rnd: couple two samplings μ, ν along a function e
that pushes μ to ν (map e μ = ν); the post must hold for every
drawn a paired with e a. Subsumes uniform/uniform_id (take μ,
ν uniform and e a bijection).
Kill a lossless left-only statement: a lossless p₀ whose output
almost-surely satisfies the post (against the unchanged right state) is
related to skip. Generalizes set_skip_left from deterministic to any
lossless program.
Swap two independent samplings on the left program.
Swap two independent samplings on the right program.
One-sided rules (EasyCrypt if⟨i⟩, kill) #
One-sided if on the left (if⟨1⟩): a left conditional on a
deterministic state guard e, related to an arbitrary right program by
relating each branch under the refined precondition.
One-sided if on the right (if⟨2⟩).
Kill a lossless right-only statement (mirror of kill_left).
The adversary / call rule (EasyCrypt call (_ : ={glob A})) #
An adversary is a program confined to a state window L (a lens); glob A
is exactly this window. Running it from two states that agree on the window
returns equal results and states that again agree on the window —
={glob A} ⟹ ={res, glob A}. The coupling is the diagonal one through L:
run the inner program once and write its result back into both states.
Adversary call, constructive form: L.lift P (an adversary acting
through window L) from L-agreeing states gives equal results and
L-agreeing states.
Adversary call, abstract form: any A confined to the window L
(A.inFootprint L.footprint) satisfies the same rule, via the factorization
A = L.lift (L.factor A). This is the modular adversary principle.
Smoke tests #
Completeness (relE → prhl2) #
The converse of prhl2.to_relE — that the wp-lifting judgment yields a
coupling — is discrete Strassen (the coupling-lifting theorem), proved
in GaudisCrypt/Strassen.lean as PMF.exists_coupling_of_hall.
The wp judgment gives Hall's marginal-domination condition directly:
plugging in indicator post-conditions turns rel into it (rel.hall
below). Strassen's theorem says this condition is also sufficient for a
coupling.
Hall's condition from rel (the necessary half of discrete
Strassen): the mass c places on any set A is dominated by the mass
d places on the Post-image of A. The converse (Hall ⇒ coupling)
is SubProbability.exists_coupling_of_hall.
For a two-sided relE, Hall's condition holds in both directions:
d's mass on B is dominated by c's mass on the Post-preimage of
B.
Scaling a sub-probability (for the mass-normalization reduction) #
Scale a sub-probability by c (well-defined as a sub-probability when
c · (total mass) ≤ 1).
Equations
- GaudisCrypt.SubProbability.scale c ν h = ⟨c • ↑ν, ⋯⟩
Instances For
Coupling lifting, sub-probability form — derived from discrete
Strassen (PMF.exists_coupling_of_hall, GaudisCrypt/Strassen.lean) by
mass normalization. Two-sided Hall forces equal total mass; the zero-mass
case is the empty coupling, and otherwise we normalize both sides to
probability measures, transfer them to PMFs via SubProbability.toPMF,
apply Strassen, and scale the resulting coupling back.
ProgramDenotation.rel.hall shows the hypotheses are exactly what relE
supplies.
Use in coupling-based program logics (the relE ↔ prhl2 correspondence
here): Barthe, Espitau, Grégoire, Hsu, Strub, "Probabilistic Couplings for
Probabilistic Reasoning", arXiv:1710.09951.
Completeness relE → prhl2: the wp-lifting judgment yields a
coupling. Extracts Hall's condition in both directions from relE via
rel.hall and feeds it to exists_coupling_of_hall. Together with
prhl2.to_relE this shows the two logics coincide.
The two relational logics coincide.
Footprint-confined adversary self-coupling #
A program confined to a Footprint R self-couples under ={glob} — from states agreeing on the
touched content it returns equal results and states again agreeing on the touched content. The
EqvGen-closure of a single deterministic Rᶜ-step, glued by coupling transitivity.
Adversary rule — single Rᶜ-step (footprint glob). A program confined to R, run from
a state σ and its image f σ under one deterministic Rᶜ-update, self-couples: equal
results, output states again one Rᶜ-step apart. This is the base case of the ={glob A}
adversary rule for glob A = R.touched_getter. The full rule is its EqvGen closure over the
Rᶜ-orbit.
Adversary rule (full), footprint glob. A program confined to R self-couples under
={glob A} (with glob A = R.touched_getter): from states agreeing on glob A it returns
equal results and states that again agree on glob A. The EqvGen-closure of
adversary_couple_step — refl/symm/trans on prhl2 (the trans = coupling gluing,
ProgramDenotation.prhl2.trans, discrete disintegration).
Coupling through a lossless, projection-preserving tail, over a base
coupling. Given a self-coupling of p under P with equal results and
g-equal finals, extending the right leg by a lossless tail c that
preserves g on its support couples p against q' = p; c (result kept)
with the same pre/post. The equal-initial-states version
(prhl2_of_lossless_tail_proj) is the instance at the diagonal coupling.
Coupling through a lossless, projection-preserving tail (equal initial
states): the diagonal instance of prhl2_of_lossless_tail_proj_inv.
Converts distribution-level transfer equations
(Lib/RO/TransferConvert.lean) into prhl2.
Coupling through a lossless tail. If q equals p followed by a lossless state-only
post-processor c (which keeps p's result), then p and q couple from equal initial
states with equal results: route the diagonal coupling of p through c on the right leg.
The projection-free instance of prhl2_of_lossless_tail_proj.