clutch.gen_diffpriv.lib.exponential
Reusable one-sided-exponential-noise library for the generic DP logic — the
exponential-mechanism analogue of lib.laplace.
Importing this file and providing a SampleIn exp_family S instance makes
the Exp surface notation and the wp_couple_exp mechanism rule available
at WHATEVER index the family occupies in S (recovered from SampleIn via
sample_idx, never hardcoded). As with lib.laplace, a client needs only:
Definition Sexp : Sig := ...; exp_family; ....
[global] Instance : SampleIn exp_family Sexp := ltac:(solve_SampleIn).
Section foo. Context `{!diffprivGS Sexp Σ}.
and then writes [Exp num den mean] in programs and applies [wp_couple_exp].
The ONLY mathematical difference from the Laplace library is the per-draw
coupling [DPcoupl_exp_draw]: the one-sided exponential supports the shift
coupling [z' = z + k] only when the shift is *upward* enough to stay in the
(one-sided) target support — [0 ≤ k + loc - loc'] — and the cost is then
[(k+loc-loc')]-bounded, [k + loc - loc' ≤ k'] giving cost [k'·ε]. Contrast
Laplace (two-sided): it needs only [|k+loc-loc'| ≤ k'] and no directionality.
This directionality is exactly what makes it the exponential mechanism.
From iris.proofmode Require Import proofmode.
From clutch.prob Require Import differential_privacy distribution exponential couplings couplings_app couplings_dp.
From clutch.gen_diffpriv Require Export primitive_laws coupling_rules proofmode.
From clutch.gen_prob_lang Require Export families.
From iris.prelude Require Import options.
Local Open Scope R.
From clutch.prob Require Import differential_privacy distribution exponential couplings couplings_app couplings_dp.
From clutch.gen_diffpriv Require Export primitive_laws coupling_rules proofmode.
From clutch.gen_prob_lang Require Export families.
From iris.prelude Require Import options.
Local Open Scope R.
Exp num den mean tape samples the one-sided exponential family at
parameter (num, den, mean) using sample tape tape (#() for a direct,
tape-less sample). Same 4-argument surface form as Laplace; the family's
index in the ambient signature is recovered from SampleIn exp_family _.
Notation Exp num den mean tape :=
(Sample (sample_idx (D := exp_family)) (Pair num (Pair den mean)) tape)
(only parsing).
(Sample (sample_idx (D := exp_family)) (Pair num (Pair den mean)) tape)
(only parsing).
Value-form of Exp (direct, tape-less): the (num,den,mean) parameters
already reduced to a PairV triple, as they appear AFTER wp_pures/tp_pures
(which evaluate the Pair into a PairV). The coupling rules are stated on
this shape so their preconditions read cleanly and match post-reduction goals;
the couple_exp tactic relies on it. Mirrors LaplaceV in lib.laplace.
Notation ExpV num den mean :=
(Sample (sample_idx (D := exp_family))
(Val (PairV (LitV (LitInt num)) (PairV (LitV (LitInt den)) (LitV (LitInt mean)))))
(Val (LitV LitUnit)))
(only parsing).
Section exponential.
Context {S : Sig} `{!SampleIn exp_family S} `{!diffprivGS S Σ}.
Local Notation fill := (@ectx_language.fill (gen_ectx_lang S)).
(* the family's index in S, recovered from the SampleIn instance *)
Local Notation lidx := (@sample_idx exp_family S _).
(Sample (sample_idx (D := exp_family))
(Val (PairV (LitV (LitInt num)) (PairV (LitV (LitInt den)) (LitV (LitInt mean)))))
(Val (LitV LitUnit)))
(only parsing).
Section exponential.
Context {S : Sig} `{!SampleIn exp_family S} `{!diffprivGS S Σ}.
Local Notation fill := (@ectx_language.fill (gen_ectx_lang S)).
(* the family's index in S, recovered from the SampleIn instance *)
Local Notation lidx := (@sample_idx exp_family S _).
The core one-sided-exponential draw coupling, on the family's outcome type
Z: shifting the location by k couples z ↦ z + k at cost k'·ε when
the shift is upward into the target support (0 ≤ k+loc-loc') and bounded
(k+loc-loc' ≤ k'). Family-level fact (independent of S), discharged by
the reusable Mcoupl_exp.
Lemma DPcoupl_exp_draw (num den loc loc' k k' : Z)
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : (0 < IZR num / IZR den)%R) :
DPcoupl (sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc'))
(λ z z', (z + k = z')%Z) (IZR k' * (IZR num / IZR den)) 0.
Proof.
simpl. rewrite /exp_rat.
destruct (decide (0 < IZR num / IZR den)) as [εpos | nε]; [|lra].
apply Mcoupl_to_DPcoupl.
by apply (Mcoupl_exp (mkposreal _ εpos) loc loc' k k' Hdir Hdist).
Qed.
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : (0 < IZR num / IZR den)%R) :
DPcoupl (sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc'))
(λ z z', (z + k = z')%Z) (IZR k' * (IZR num / IZR den)) 0.
Proof.
simpl. rewrite /exp_rat.
destruct (decide (0 < IZR num / IZR den)) as [εpos | nε]; [|lra].
apply Mcoupl_to_DPcoupl.
by apply (Mcoupl_exp (mkposreal _ εpos) loc loc' k k' Hdir Hdist).
Qed.
The one-sided exponential mechanism at the WP level: sampling at location
loc (impl) vs loc' (spec) couples to outputs that agree after shifting
the spec by k, at multiplicative error k'·ε, under the one-sided
directionality. Obtained by instantiating the GENERIC prog-couple seam
wp_couple_sample_gen with DPcoupl_exp_draw at index sample_idx.
Lemma wp_couple_exp (loc loc' k k' : Z)
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(num den : Z) (ε ε' : R) K E :
IZR num / IZR den = ε →
0 < IZR num / IZR den →
ε' = (IZR k' * ε) →
{{{ ⤇ fill K (Sample lidx
(Val (PairV (LitV (LitInt num))
(PairV (LitV (LitInt den)) (LitV (LitInt loc')))))
(Val (LitV LitUnit))) ∗ ↯m ε' }}}
Sample lidx
(Val (PairV (LitV (LitInt num))
(PairV (LitV (LitInt den)) (LitV (LitInt loc)))))
(Val (LitV LitUnit)) @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Hε εpos Hε' Φ) "(Hr & Hε) HΦ".
iApply (wp_couple_sample_gen S lidx
(sf_param_to_val exp_family (num, den, loc))
(sf_param_to_val exp_family (num, den, loc'))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc)))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc')))
(λ v v', ∃ z : Z, v = #z ∧ v' = #(z + k)) K E ε'
(sig_sample_at exp_family S (num, den, loc))
(sig_sample_at exp_family S (num, den, loc')) _ with "[$Hr $Hε]").
{ iIntros "!>" (v v') "(Hspec & %HR)". destruct HR as (z & -> & ->).
iApply ("HΦ" $! z with "Hspec"). }
(* the single DP obligation: lift the exponential draw coupling through sf_inj *)
Unshelve.
apply DPcoupl_map; [subst ε'; apply Rmult_le_pos; [apply IZR_le; lia | lra] | lra | ].
eapply (DPcoupl_mono _ _ _ _ (λ z z', (z + k = z')%Z) _ ε' ε' 0 0);
[ intros; reflexivity
| intros; reflexivity
| intros z z' Hzz'; exists z; split; [done | by rewrite Hzz']
| lra | lra
| subst ε'; rewrite -Hε; by apply DPcoupl_exp_draw ].
Qed.
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(num den : Z) (ε ε' : R) K E :
IZR num / IZR den = ε →
0 < IZR num / IZR den →
ε' = (IZR k' * ε) →
{{{ ⤇ fill K (Sample lidx
(Val (PairV (LitV (LitInt num))
(PairV (LitV (LitInt den)) (LitV (LitInt loc')))))
(Val (LitV LitUnit))) ∗ ↯m ε' }}}
Sample lidx
(Val (PairV (LitV (LitInt num))
(PairV (LitV (LitInt den)) (LitV (LitInt loc)))))
(Val (LitV LitUnit)) @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Hε εpos Hε' Φ) "(Hr & Hε) HΦ".
iApply (wp_couple_sample_gen S lidx
(sf_param_to_val exp_family (num, den, loc))
(sf_param_to_val exp_family (num, den, loc'))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc)))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc')))
(λ v v', ∃ z : Z, v = #z ∧ v' = #(z + k)) K E ε'
(sig_sample_at exp_family S (num, den, loc))
(sig_sample_at exp_family S (num, den, loc')) _ with "[$Hr $Hε]").
{ iIntros "!>" (v v') "(Hspec & %HR)". destruct HR as (z & -> & ->).
iApply ("HΦ" $! z with "Hspec"). }
(* the single DP obligation: lift the exponential draw coupling through sf_inj *)
Unshelve.
apply DPcoupl_map; [subst ε'; apply Rmult_le_pos; [apply IZR_le; lia | lra] | lra | ].
eapply (DPcoupl_mono _ _ _ _ (λ z z', (z + k = z')%Z) _ ε' ε' 0 0);
[ intros; reflexivity
| intros; reflexivity
| intros z z' Hzz'; exists z; split; [done | by rewrite Hzz']
| lra | lra
| subst ε'; rewrite -Hε; by apply DPcoupl_exp_draw ].
Qed.
The surface-form exponential coupling rule, stated on the Exp notation
(with the (num,den,loc) parameter as an un-reduced Pair).
Lemma hoare_couple_exp (loc loc' k k' : Z)
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(num den : Z) (ε ε' : R) K E :
IZR num / IZR den = ε →
0 < IZR num / IZR den →
ε' = (IZR k' * ε) →
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m ε' }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Hε εpos Hε' Φ) "(Hr & Hε) HΦ".
wp_pures. tp_pures.
iApply (wp_couple_exp loc loc' k k' Hdir Hdist num den ε ε' K E Hε εpos Hε'
with "[$Hr $Hε]").
iApply "HΦ".
Qed.
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(num den : Z) (ε ε' : R) K E :
IZR num / IZR den = ε →
0 < IZR num / IZR den →
ε' = (IZR k' * ε) →
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m ε' }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Hε εpos Hε' Φ) "(Hr & Hε) HΦ".
wp_pures. tp_pures.
iApply (wp_couple_exp loc loc' k k' Hdir Hdist num den ε ε' K E Hε εpos Hε'
with "[$Hr $Hε]").
iApply "HΦ".
Qed.
The exact (error-free) coupling: identical locations couple perfectly
along the diagonal at zero cost (the reflexive coupling ARcoupl_eq
lifted to DPcoupl). No directionality needed here (k = 0).
Lemma hoare_couple_exp_exact (loc num den : Z) K E :
{{{ ⤇ fill K (Exp #num #den #loc #()) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #z }}}.
Proof.
iIntros (Φ) "Hr HΦ".
wp_pures. tp_pures.
iMod ecm_zero as "Hε".
iApply (wp_couple_sample_gen S lidx
(sf_param_to_val exp_family (num, den, loc))
(sf_param_to_val exp_family (num, den, loc))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc)))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc)))
(λ v v', ∃ z : Z, v = #z ∧ v' = #z) K E 0
(sig_sample_at exp_family S (num, den, loc))
(sig_sample_at exp_family S (num, den, loc)) _ with "[$Hr $Hε]").
{ iIntros "!>" (v v') "(Hspec & %HR)". destruct HR as (z & -> & ->).
iApply ("HΦ" $! z with "Hspec"). }
(* the reflexive coupling of the exponential draw, lifted through sf_inj *)
Unshelve.
apply (DPcoupl_map (sf_inj exp_family) (sf_inj exp_family)
(sf_sample exp_family (num, den, loc)) (sf_sample exp_family (num, den, loc))
(λ v v', ∃ z : Z, v = #z ∧ v' = #z) 0 0); [lra | lra | ].
eapply (DPcoupl_mono (sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc))
(=) (λ a a', ∃ z : Z, sf_inj exp_family a = #z ∧ sf_inj exp_family a' = #z)
0 0 0 0);
[ intros; reflexivity | intros; reflexivity
| intros a a' ->; by exists a' | lra | lra
| apply ARcoupl_to_DPcoupl, ARcoupl_eq ].
Qed.
End exponential.
{{{ ⤇ fill K (Exp #num #den #loc #()) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #z }}}.
Proof.
iIntros (Φ) "Hr HΦ".
wp_pures. tp_pures.
iMod ecm_zero as "Hε".
iApply (wp_couple_sample_gen S lidx
(sf_param_to_val exp_family (num, den, loc))
(sf_param_to_val exp_family (num, den, loc))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc)))
(dmap (sf_inj exp_family) (sf_sample exp_family (num, den, loc)))
(λ v v', ∃ z : Z, v = #z ∧ v' = #z) K E 0
(sig_sample_at exp_family S (num, den, loc))
(sig_sample_at exp_family S (num, den, loc)) _ with "[$Hr $Hε]").
{ iIntros "!>" (v v') "(Hspec & %HR)". destruct HR as (z & -> & ->).
iApply ("HΦ" $! z with "Hspec"). }
(* the reflexive coupling of the exponential draw, lifted through sf_inj *)
Unshelve.
apply (DPcoupl_map (sf_inj exp_family) (sf_inj exp_family)
(sf_sample exp_family (num, den, loc)) (sf_sample exp_family (num, den, loc))
(λ v v', ∃ z : Z, v = #z ∧ v' = #z) 0 0); [lra | lra | ].
eapply (DPcoupl_mono (sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc))
(sf_sample exp_family (num, den, loc))
(=) (λ a a', ∃ z : Z, sf_inj exp_family a = #z ∧ sf_inj exp_family a' = #z)
0 0 0 0);
[ intros; reflexivity | intros; reflexivity
| intros a a' ->; by exists a' | lra | lra
| apply ARcoupl_to_DPcoupl, ARcoupl_eq ].
Qed.
End exponential.
The one-sided-exponential coupling tactics follow the consolidated design of
lib.laplace: TWO tactics — the bundled couple_exp and the apply-only
couple_exp_apply — built from the family-agnostic couple_bind /
couple_discharge / couple_discharge_apply (defined in
gen_diffpriv.proofmode) plus the thin family-specific couple_exp_iapply.
The old couple_exp_cost is subsumed by couple_exp: the routed (non-exact
cost) regime is selected by the caller's \"[$Hr Hε]\" pattern, not by a
separate tactic. The only family-specific wrinkle is the side-conditions: the
one-sided exponential carries DIRECTIONALITY premises Hdir : 0 ≤ k+loc-loc'
and Hdist : k+loc-loc' ≤ k' (no absolute value, unlike two-sided Laplace) —
both closed by the lia alternative in the shared batteries (typically with
the in-context adjacency hypothesis).
couple_exp_iapply k k' pat — the family-specific apply step shared by
couple_exp and couple_exp_apply. unshelve (the tactical, not the
Unshelve command) turns ONLY the goals THIS iApply shelves — the
directionality side-conditions Hdir/Hdist — into regular front goals.
Ltac couple_exp_iapply k k' pat :=
unshelve (iApply (wp_couple_exp _ _ k k' _ _ _ _ _ _ _ _ with pat)).
unshelve (iApply (wp_couple_exp _ _ k k' _ _ _ _ _ _ _ _ with pat)).
couple_exp k k' with "[…]" — the ergonomic coupling step for the one-sided
exponential. Focuses the Sample on both sides (couple_bind) and applies
the value-form wp_couple_exp inferring loc/loc'/num/den/ε/ε'/K/E from the
goal — the author supplies only the privacy choice k (shift) and k' (cost
bound) and the resource pattern. Subsumes the old exact-cost couple_exp
(eager-frame the credit, \"[$Hr $Hε]\") and the non-exact couple_exp_cost
(route the credit unframed, \"[$Hr Hε]\", leaving ↯m (k'·ε) for an
ecm_eq/ecm_weaken); the regime is chosen by the resource pattern.
Tactic Notation "couple_exp" uconstr(k) uconstr(k') "with" constr(pat) :=
couple_bind; couple_exp_iapply k k' pat; couple_discharge.
couple_bind; couple_exp_iapply k k' pat; couple_discharge.
couple_exp_apply k k' with "[$Hr Hε]" — the APPLY-ONLY variant, for
INTERLEAVED coupling sites where essential setup happens BETWEEN focusing the
Sample and applying the coupling rule. PRECONDITION: the caller has ALREADY
focused the Sample on both sides (its own couple_bind-equivalent) and done
any interleaved setup; this tactic runs ONLY the unshelve (iApply …) + the
slimmer couple_discharge_apply battery, leaving the credit goal and the
hand-closed residual goals (and postcondition) for the caller.
Tactic Notation "couple_exp_apply" uconstr(k) uconstr(k') "with" constr(pat) :=
couple_exp_iapply k k' pat; couple_discharge_apply.
Section exponential_canary.
Context {Sg : Sig} `{!SampleIn exp_family Sg} `{!diffprivGS Sg Σ}.
Local Notation fill := (@ectx_language.fill (gen_ectx_lang Sg)).
couple_exp_iapply k k' pat; couple_discharge_apply.
Section exponential_canary.
Context {Sg : Sig} `{!SampleIn exp_family Sg} `{!diffprivGS Sg Σ}.
Local Notation fill := (@ectx_language.fill (gen_ectx_lang Sg)).
CANARY: two surface-form Exp #num #den #loc #() draws couple, at cost
k'·(num/den), with the spec output the impl output shifted by k —
driven entirely by the couple_exp tactic. Stated over the Exp notation
(i.e. expr, with un-reduced Pair params) so it exercises the surface
path a program takes. The directionality side-conditions 0 ≤ k+loc-loc'
and k+loc-loc' ≤ k' are passed as hypotheses Hdir/Hdist and closed by
the tactic via assumption/lia; demonstrates the convenience layer end to
end including the exponential's one-sided side-conditions.
Lemma wp_exp_shift_canary (loc loc' k k' num den : Z)
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : 0 < IZR num / IZR den) K E :
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m (IZR k' * (IZR num / IZR den)) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
(* the spec resource ⤇ must be a standalone hypothesis before tp_pures/
tp_bind (inside couple_exp) can step the spec thread — so destructure
the bundled precondition first, exactly as the hoare_* clients do. *)
iIntros (Φ) "(Hr & Hε) HΦ".
couple_exp k k' with "[$Hr $Hε]".
iApply "HΦ".
Qed.
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : 0 < IZR num / IZR den) K E :
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m (IZR k' * (IZR num / IZR den)) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
(* the spec resource ⤇ must be a standalone hypothesis before tp_pures/
tp_bind (inside couple_exp) can step the spec thread — so destructure
the bundled precondition first, exactly as the hoare_* clients do. *)
iIntros (Φ) "(Hr & Hε) HΦ".
couple_exp k k' with "[$Hr $Hε]".
iApply "HΦ".
Qed.
CANARY for the NON-EXACT cost regime of the MERGED couple_exp: identical
statement to the framed canary above, but the credit is ROUTED (unframed
Hε, pattern \"[$Hr Hε]\") into the residual ↯m (k'·ε) goal rather than
$-framed. Here the cost is exact so the residual goal is closed by simply
re-framing Hε; a real cost-reconciliation site would instead run
iApply ecm_eq; … / iApply ecm_weaken; … there. Exercises that the SAME
couple_exp tactic, when handed a routed pattern, elaborates,
auto-discharges the directionality/equational side-conditions, and leaves a
clean credit goal (the old separate couple_exp_cost is now subsumed).
Lemma wp_exp_shift_canary_cost (loc loc' k k' num den : Z)
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : 0 < IZR num / IZR den) K E :
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m (IZR k' * (IZR num / IZR den)) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Φ) "(Hr & Hε) HΦ".
couple_exp k k' with "[$Hr Hε]".
(* residual credit goal ↯m (k'·ε) — closed here by re-framing the routed
Hε (exact cost); a non-exact site would iApply ecm_* instead *)
2: iApply "HΦ".
iFrame "Hε".
Qed.
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : 0 < IZR num / IZR den) K E :
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m (IZR k' * (IZR num / IZR den)) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Φ) "(Hr & Hε) HΦ".
couple_exp k k' with "[$Hr Hε]".
(* residual credit goal ↯m (k'·ε) — closed here by re-framing the routed
Hε (exact cost); a non-exact site would iApply ecm_* instead *)
2: iApply "HΦ".
iFrame "Hε".
Qed.
CANARY for the APPLY-ONLY couple_exp_apply: identical statement, but the
caller does the wp_bind/tp_bind MANUALLY (mimicking an interleaved
site) before calling the apply-only tactic — exercising that
couple_exp_apply does NOT itself re-bind the Sample and works once the
caller has focused both sides. The credit is ROUTED (unframed Hε,
pattern \"[$Hr Hε]\") into the residual ↯m (k'·ε) goal; here the cost is
exact so the residual is closed by re-framing Hε.
Lemma wp_exp_shift_canary_apply (loc loc' k k' num den : Z)
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : 0 < IZR num / IZR den) K E :
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m (IZR k' * (IZR num / IZR den)) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Φ) "(Hr & Hε) HΦ".
(* MANUAL bind on both sides — the interleaved-site idiom the apply-only
tactic supports (and where setup could be interposed here) *)
wp_pures. tp_pures.
wp_bind (Sample _ _ _). tp_bind (Sample _ _ _).
couple_exp_apply k k' with "[$Hr Hε]".
(* residual credit goal ↯m (k'·ε) — closed here by re-framing the routed
Hε (exact cost); a non-exact site would iApply ecm_* instead *)
2: iApply "HΦ".
iFrame "Hε".
Qed.
End exponential_canary.
(Hdir : (0 <= k + loc - loc')%Z)
(Hdist : (k + loc - loc' <= k')%Z)
(Hpos : 0 < IZR num / IZR den) K E :
{{{ ⤇ fill K (Exp #num #den #loc' #()) ∗ ↯m (IZR k' * (IZR num / IZR den)) }}}
Exp #num #den #loc #() @ E
{{{ (z : Z), RET #z; ⤇ fill K #(z + k) }}}.
Proof.
iIntros (Φ) "(Hr & Hε) HΦ".
(* MANUAL bind on both sides — the interleaved-site idiom the apply-only
tactic supports (and where setup could be interposed here) *)
wp_pures. tp_pures.
wp_bind (Sample _ _ _). tp_bind (Sample _ _ _).
couple_exp_apply k k' with "[$Hr Hε]".
(* residual credit goal ↯m (k'·ε) — closed here by re-framing the routed
Hε (exact cost); a non-exact site would iApply ecm_* instead *)
2: iApply "HΦ".
iFrame "Hε".
Qed.
End exponential_canary.