clutch.eris.lib.sampling.geometric.implementation
From clutch.eris Require Import eris.
From clutch.eris.lib.sampling Require Import utils.
From clutch.eris.lib.sampling.bernoulli Require Import interface.
From clutch.eris.lib.sampling.geometric Require Import interface.
Section Tape.
#[local] Open Scope fin.
Fixpoint is_geometric_translation (b : list (fin 2)) (g : list nat) :=
match g with
| [] => b = []
| n::g =>
∃ suf,
b = (repeat 0 n ++ [1]) ++ suf ∧
is_geometric_translation suf g
end.
Lemma is_geometric_translation_nil : is_geometric_translation [] [].
Proof. reflexivity. Qed.
Lemma is_geometric_translation_snoc (b : list (fin 2)) (g : list nat) (n : nat) :
is_geometric_translation b g → is_geometric_translation (b ++ repeat 0%fin n ++ [1%fin]) (g ++ [n]).
Proof.
elim:g b n =>[|hg tg IH] b n /= is_tl.
- subst. eexists. split; last reflexivity. rewrite app_nil_r //.
- destruct is_tl as (suf & -> & is_tl).
eexists.
rewrite -!app_assoc.
split; first reflexivity.
by apply IH.
Qed.
End Tape.
#[local] Open Scope R.
Section Geometric.
Set Default Proof Using "Type*".
Context `{!bernoulli_spec bernoulli balloc}.
Definition own_geometric_tape `{!erisGS Σ} (α : loc) (N M : nat) (t : list nat) : iProp Σ :=
∃ l,
own_bernoulli_tape α N M l ∗
⌜is_geometric_translation l t⌝.
#[local] Ltac done ::= solve[
lia |
lra |
nra |
tactics.done |
auto
].
Definition geometric_tape : val :=
rec: "geometric_tape" "α" "N" "M" :=
if: bernoulli "α" "N" "M" = #1 then #0 else
#1 + "geometric_tape" "α" "N" "M"
.
Definition geometric : expr := geometric_tape #().
Definition geometric_alloc : val := balloc.
Lemma ec_geometric_split :
∀ (p q : nat) (D : nat → R) (L : R),
(0 < p ≤ q + 1)%nat →
(∀ k, 0 <= D k <= L)%R →
let ε := SeriesC (λ k, geometric_prob p q k * D k)%R in
let ε' := SeriesC (λ k, geometric_prob p q k * D (S k))%R in
(ε = (p / (q + 1) * D 0%nat + (1 - (p / (q + 1))) * ε'))%R.
Proof.
move=>p q D L p_bounds D_bounds ε ε'.
unfold ε, ε'.
set (s := (p / (q + 1))%R).
set (t := (1 - s)%R).
move=>[:ex_series_geom_D].
rewrite SeriesC_first_nat; last first.
{ abstract: ex_series_geom_D.
apply (ex_seriesC_le _ (λ k, geometric_prob p q k * L)).
{ move=>k.
split.
{ apply Rmult_le_pos; last apply D_bounds.
apply geometric_prob_pos.
lia.
}
{ apply Rmult_le_compat_l; last apply D_bounds.
apply geometric_prob_pos.
lia.
}
}
apply ex_seriesC_scal_r.
eexists.
apply is_seriesC_geometric_prob.
lia.
}
unfold geometric_prob at 1.
rewrite pow_O Rmult_1_r.
fold s.
f_equal.
rewrite -SeriesC_scal_l.
apply SeriesC_ext.
move=>k /=.
rewrite geometric_prob_S.
fold s t.
lra.
Qed.
Lemma twp_geometric_split `{!erisGS Σ} :
∀ (p q : nat),
(0 < p)%nat →
(p ≤ q + 1)%nat →
∀ (D : nat → R) (L : R) (ε : R),
(∀ (n : nat), 0 <= D n <= L)%R →
SeriesC (λ k, (geometric_prob p q k * D k)%R) = ε →
↯ ε -∗ WP geometric #p #q [{ v, ∃ (k : nat), ⌜v = #k⌝ ∗ ↯ (D k) }].
Proof.
iIntros (p q Hp Hpq D L ε HD HSum) "Herr".
iApply twp_err_pos; auto.
iIntros (ε_term Hε_term) "Hterm".
destruct (Nat.eq_dec p (q + 1)%nat) as [-> | p_ne_q_add_1].
{ rewrite (ec_geometric_split _ _ _ _ _ HD) in HSum; last done.
pose proof (pos_INR q).
rewrite plus_INR Rdiv_diag in HSum; last (simpl; lra).
rewrite Rminus_diag Rmult_1_l Rmult_0_l Rplus_0_r in HSum.
subst.
unfold geometric, geometric_tape.
wp_pures.
iMod ec_zero as "Herr0".
wp_apply (bernoulli_success_spec_simple with "[Herr0]") as "_".
{ iApply (ec_eq with "Herr0").
rewrite S_INR plus_INR Rdiv_diag; last simpl; lra.
}
wp_pures.
iModIntro.
by iFrame.
}
assert (p < q + 1)%nat as p_lt_Sq by lia.
apply lt_INR in p_lt_Sq as p_lt_Sq'.
apply lt_INR in Hp as Hp'.
rewrite plus_INR /= in p_lt_Sq'.
simpl in Hp'.
rewrite /geometric /geometric_tape.
iRevert (D ε HD HSum) "Herr".
set (s := (p / (q + 1))%R).
set (t := ((1 - s)%R)).
assert (0 < s).
{ apply Rdiv_lt_0_compat; lra. }
assert (0 < t).
{ rewrite Rlt_0_minus -Rcomplements.Rdiv_lt_1; lra. }
assert (1 < / t).
{ unfold t, s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar; first done.
rewrite Rcomplements.Rlt_minus_l -{1}(Rplus_0_r 1).
apply Rplus_le_lt_compat; first reflexivity.
apply Rdiv_lt_0_compat; lra.
}
assert (1 < / s).
{ unfold s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar.
{ apply Rdiv_lt_0_compat; lra. }
{ rewrite -Rcomplements.Rdiv_lt_1; lra. }
}
iApply (ec_ind_amp _ (/ t) with "[] Hterm"); try done.
iModIntro.
iIntros (ε' Hε') "IH Hterm".
iIntros (D ε HD HDε) "Herr".
set (ε0 := SeriesC (λ k : nat, geometric_prob p q k * D (S k))).
assert (0 <= ε0).
{ unfold ε0.
apply SeriesC_ge_0'.
move=>k.
apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
wp_pures.
iPoseProof (ec_combine with "[Hterm Herr]") as "Hec"; first iFrame.
wp_apply (twp_bernoulli_scale _ _ _ (ε0 + ε' / t) (D 0%nat) with "Hec")
as (?) "[[-> Herr] | [-> Herr]]"; first lia.
{ by apply Rplus_le_le_0_compat; last nra. }
{ apply HD. }
{ rewrite -HDε SeriesC_first_nat; last first.
{ apply (ex_seriesC_le _ (λ k, geometric_prob p q k * L)).
{ move=>k.
split.
{ apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
{ apply Rmult_le_compat_l; last apply HD.
apply geometric_prob_pos.
lia.
}
}
apply ex_seriesC_scal_r.
eexists.
apply is_seriesC_geometric_prob.
lia.
}
unfold geometric_prob at 1.
rewrite pow_O.
erewrite SeriesC_ext; last first.
{ move=>k /=.
rewrite geometric_prob_S Rmult_assoc.
fold s t.
reflexivity.
}
rewrite SeriesC_scal_l S_INR /=.
fold ε0 s t.
rewrite (Rmult_plus_distr_r _ _ t) Rdiv_def Rmult_assoc Rinv_l; lra.
}
{
do 2 wp_pure.
iPoseProof (ec_split with "Herr") as "[Herr Hterm]"; first assumption.
{ apply Rmult_le_pos; lra. }
rewrite (Rdiv_def ε' t) (Rmult_comm ε' (/ t)).
iSpecialize ("IH" with "Hterm").
iPoseProof ("IH" $! (D ∘ S) with "[] [] Herr") as "IHH"; try done.
{ iPureIntro. move=>k. apply HD. }
wp_bind (((rec: _ _ := _) _ _)%V _)%E.
iApply tgl_wp_wand_r.
iSplitL "IHH"; first done.
iIntros (v) "(%k & -> & Herr)".
wp_pures.
iModIntro.
iExists (S k).
iFrame.
iPureIntro.
f_equal.
rewrite (Nat2Z.inj_add 1 k) //.
}
{ wp_pures.
iModIntro.
by iFrame.
}
Qed.
Lemma twp_geometric_alloc `{!erisGS Σ} (p q : nat) :
[[{ True }]]
geometric_alloc #p #q
[[{ (α : loc), RET #lbl:α; own_geometric_tape α p q [] }]].
Proof.
iIntros (Φ) "_ HΦ".
unfold geometric_alloc.
wp_apply (twp_bernoulli_alloc with "[$]") as (α) "Hα".
iApply ("HΦ" with "[$Hα]").
by iPureIntro.
Qed.
Lemma twp_geometric_presample_adv_comp `{!erisGS Σ} :
∀ (p q : nat) (α : loc) (ns : list nat)
(e : expr)
(D : nat → R) (L : R) (ε : R)
(Φ : val → iProp Σ),
(0 < p)%nat →
(p ≤ q + 1)%nat →
to_val e = None →
(∀ (n : nat), 0 <= D n <= L)%R →
SeriesC (λ k, (geometric_prob p q k * D k)%R) = ε →
own_geometric_tape α p q ns ∗
↯ ε ∗
(∀ (i : nat), ↯ (D i) ∗ own_geometric_tape α p q (ns ++ [i]) -∗ WP e [{ Φ }])
⊢ WP e [{ Φ }].
Proof.
iIntros (p q α ns e D L ε Φ Hp Hpq He HD HSum)
"((%l & Htape & %is_tl) & Herr & Hnext)".
iApply twp_err_pos; auto.
iIntros (ε_term Hε_term) "Hterm".
destruct (Nat.eq_dec p (q + 1)%nat) as [-> | p_ne_q_add_1].
{ rewrite (ec_geometric_split _ _ _ _ _ HD) in HSum; last done.
pose proof (pos_INR q).
rewrite plus_INR Rdiv_diag in HSum; last (simpl; lra).
rewrite Rminus_diag Rmult_1_l Rmult_0_l Rplus_0_r in HSum.
subst.
unfold geometric, geometric_tape.
wp_pures.
set (D' (k : fin 2) := match k with
| Fin.F1 _ => 1%R
| FS _ _ => D 0%nat
end).
wp_apply (twp_presample_bernoulli_adv_comp _ _ _ (q + 1)%nat q _ _ D' with "[$Herr $Htape Hnext]") as (k) "[Herr Htape]"; try done.
{ move=>k.
inv_fin k=>[|?] /=; first lra.
apply HD.
}
{ rewrite plus_INR /= Rdiv_diag; lra. }
inv_fin k=>[|k]/=.
{ iDestruct (ec_contradict with "Herr") as "[]". reflexivity. }
inv_fin k=>[|k]/=; last inv_fin k.
wp_apply "Hnext".
iFrame.
iPureIntro.
by apply (is_geometric_translation_snoc _ _ 0).
}
assert (p < q + 1)%nat as p_lt_Sq by lia.
apply lt_INR in p_lt_Sq as p_lt_Sq'.
apply lt_INR in Hp as Hp'.
rewrite plus_INR /= in p_lt_Sq'.
simpl in Hp'.
rewrite /geometric /geometric_tape -(app_nil_r l).
replace [] with (repeat (0%fin : fin 2) 0) by reflexivity.
iAssert (∀ i : nat,
↯ (D i) ∗
own_geometric_tape α p q (ns ++ [0 + i]%nat) -∗
WP e [{ v, Φ v }])%I with "Hnext" as "Hnext".
generalize 0%nat at 4 5 as n.
iIntros (n).
iRevert (n D ε HD HSum) "Htape Herr Hnext".
set (s := (p / (q + 1))%R).
set (t := ((1 - s)%R)).
assert (0 < s).
{ apply Rdiv_lt_0_compat; lra. }
assert (0 < t).
{ rewrite Rlt_0_minus -Rcomplements.Rdiv_lt_1; lra. }
assert (1 < / t).
{ unfold t, s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar; first done.
rewrite Rcomplements.Rlt_minus_l -{1}(Rplus_0_r 1).
apply Rplus_le_lt_compat; first reflexivity.
apply Rdiv_lt_0_compat; lra.
}
assert (1 < / s).
{ unfold s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar.
{ apply Rdiv_lt_0_compat; lra. }
{ rewrite -Rcomplements.Rdiv_lt_1; lra. }
}
iApply (ec_ind_amp _ (/ t) with "[] Hterm"); try done.
iModIntro.
iIntros (ε' Hε') "IH Hterm".
iIntros (n D ε HD HDε) "Htape Herr Hnext".
set (ε0 := SeriesC (λ k : nat, geometric_prob p q k * D (S k))).
assert (0 <= ε0).
{ unfold ε0.
apply SeriesC_ge_0'.
move=>k.
apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
wp_pures.
iPoseProof (ec_combine with "[$Hterm $Herr]") as "Hec".
set (D' (k : fin 2) := match k with
| Fin.F1 _ => (ε0 + ε' / t)%R
| FS _ _ => D 0%nat
end).
wp_apply (twp_presample_bernoulli_adv_comp _ _ _ _ _ _ _ D' with "[$Hec $Htape Hnext IH]") as (k) "[Herr Htape]"; try done.
{ move=>k.
inv_fin k=>[|?] /=; last apply HD.
by apply Rplus_le_le_0_compat; last nra.
}
{ rewrite -HDε SeriesC_first_nat; last first.
{ apply (ex_seriesC_le _ (λ k, geometric_prob p q k * L)).
{ move=>k.
split.
{ apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
{ apply Rmult_le_compat_l; last apply HD.
apply geometric_prob_pos.
lia.
}
}
apply ex_seriesC_scal_r.
eexists.
apply is_seriesC_geometric_prob.
lia.
}
unfold geometric_prob at 1.
rewrite pow_O.
erewrite SeriesC_ext; last first.
{ move=>k /=.
rewrite geometric_prob_S Rmult_assoc.
fold s t.
reflexivity.
}
rewrite SeriesC_scal_l /=.
fold ε0 s t.
rewrite (Rmult_plus_distr_r _ _ t) Rdiv_def Rmult_assoc Rinv_l; lra.
}
inv_fin k=>[|k] /=; last (inv_fin k=>[|k]; last inv_fin k).
{
iPoseProof (ec_split with "Herr") as "[Herr Hterm]"; first assumption.
{ apply Rmult_le_pos; lra. }
rewrite (Rdiv_def ε' t) (Rmult_comm ε' (/ t)) -app_assoc -repeat_cons.
iSpecialize ("IH" with "Hterm").
wp_apply ("IH" $! (S n) (D ∘ S) with "[] [] Htape Herr") as (k) "[Herr Htape]"; try done.
{ iPureIntro. move=>k. apply HD. }
wp_apply "Hnext".
iFrame.
rewrite Nat.add_succ_r //.
}
{ wp_apply "Hnext".
iFrame.
iPureIntro.
rewrite Nat.add_0_r -app_assoc.
by apply is_geometric_translation_snoc.
}
Qed.
Lemma twp_geometric_tape `{!erisGS Σ} :
∀ (p q : nat) (α : loc) (n : nat)
(ns : list nat) (Φ : val → iProp Σ),
own_geometric_tape α p q (n::ns) -∗
(own_geometric_tape α p q ns -∗ Φ #n) -∗
WP geometric_tape #lbl:α #p #q [{ Φ }].
Proof.
iIntros (p q α n).
iInduction (n) as [|n] "IH".
- iIntros (ns Φ) "(%l & Htape & %is_tl) HΦ".
destruct is_tl as (suf & -> & is_tl).
simpl.
unfold geometric_tape.
wp_pures.
wp_apply (twp_bernoulli_tape with "Htape") as "Htape".
wp_pures.
iModIntro.
by iApply ("HΦ" with "[$Htape]").
- iIntros (ns Φ) "(%l & Htape & %is_tl) HΦ".
destruct is_tl as (suf & -> & is_tl).
simpl.
unfold geometric_tape.
wp_pures.
wp_apply (twp_bernoulli_tape with "Htape") as "Htape".
do 2 wp_pure.
fold geometric_tape.
wp_apply ("IH" with "[$Htape]") as "Htape".
{ iPureIntro.
by eexists.
}
wp_pures.
rewrite -(Nat2Z.inj_add 1 n) /=.
iModIntro.
iApply ("HΦ" with "Htape").
Qed.
#[global] Instance geometric_of_bernoulli : geometric_spec geometric_tape geometric_alloc :=
GeometricSpec _ _
(@twp_geometric_split)
(@own_geometric_tape)
(@twp_geometric_alloc)
(@twp_geometric_tape)
(@twp_geometric_presample_adv_comp)
.
End Geometric.
From clutch.eris.lib.sampling Require Import utils.
From clutch.eris.lib.sampling.bernoulli Require Import interface.
From clutch.eris.lib.sampling.geometric Require Import interface.
Section Tape.
#[local] Open Scope fin.
Fixpoint is_geometric_translation (b : list (fin 2)) (g : list nat) :=
match g with
| [] => b = []
| n::g =>
∃ suf,
b = (repeat 0 n ++ [1]) ++ suf ∧
is_geometric_translation suf g
end.
Lemma is_geometric_translation_nil : is_geometric_translation [] [].
Proof. reflexivity. Qed.
Lemma is_geometric_translation_snoc (b : list (fin 2)) (g : list nat) (n : nat) :
is_geometric_translation b g → is_geometric_translation (b ++ repeat 0%fin n ++ [1%fin]) (g ++ [n]).
Proof.
elim:g b n =>[|hg tg IH] b n /= is_tl.
- subst. eexists. split; last reflexivity. rewrite app_nil_r //.
- destruct is_tl as (suf & -> & is_tl).
eexists.
rewrite -!app_assoc.
split; first reflexivity.
by apply IH.
Qed.
End Tape.
#[local] Open Scope R.
Section Geometric.
Set Default Proof Using "Type*".
Context `{!bernoulli_spec bernoulli balloc}.
Definition own_geometric_tape `{!erisGS Σ} (α : loc) (N M : nat) (t : list nat) : iProp Σ :=
∃ l,
own_bernoulli_tape α N M l ∗
⌜is_geometric_translation l t⌝.
#[local] Ltac done ::= solve[
lia |
lra |
nra |
tactics.done |
auto
].
Definition geometric_tape : val :=
rec: "geometric_tape" "α" "N" "M" :=
if: bernoulli "α" "N" "M" = #1 then #0 else
#1 + "geometric_tape" "α" "N" "M"
.
Definition geometric : expr := geometric_tape #().
Definition geometric_alloc : val := balloc.
Lemma ec_geometric_split :
∀ (p q : nat) (D : nat → R) (L : R),
(0 < p ≤ q + 1)%nat →
(∀ k, 0 <= D k <= L)%R →
let ε := SeriesC (λ k, geometric_prob p q k * D k)%R in
let ε' := SeriesC (λ k, geometric_prob p q k * D (S k))%R in
(ε = (p / (q + 1) * D 0%nat + (1 - (p / (q + 1))) * ε'))%R.
Proof.
move=>p q D L p_bounds D_bounds ε ε'.
unfold ε, ε'.
set (s := (p / (q + 1))%R).
set (t := (1 - s)%R).
move=>[:ex_series_geom_D].
rewrite SeriesC_first_nat; last first.
{ abstract: ex_series_geom_D.
apply (ex_seriesC_le _ (λ k, geometric_prob p q k * L)).
{ move=>k.
split.
{ apply Rmult_le_pos; last apply D_bounds.
apply geometric_prob_pos.
lia.
}
{ apply Rmult_le_compat_l; last apply D_bounds.
apply geometric_prob_pos.
lia.
}
}
apply ex_seriesC_scal_r.
eexists.
apply is_seriesC_geometric_prob.
lia.
}
unfold geometric_prob at 1.
rewrite pow_O Rmult_1_r.
fold s.
f_equal.
rewrite -SeriesC_scal_l.
apply SeriesC_ext.
move=>k /=.
rewrite geometric_prob_S.
fold s t.
lra.
Qed.
Lemma twp_geometric_split `{!erisGS Σ} :
∀ (p q : nat),
(0 < p)%nat →
(p ≤ q + 1)%nat →
∀ (D : nat → R) (L : R) (ε : R),
(∀ (n : nat), 0 <= D n <= L)%R →
SeriesC (λ k, (geometric_prob p q k * D k)%R) = ε →
↯ ε -∗ WP geometric #p #q [{ v, ∃ (k : nat), ⌜v = #k⌝ ∗ ↯ (D k) }].
Proof.
iIntros (p q Hp Hpq D L ε HD HSum) "Herr".
iApply twp_err_pos; auto.
iIntros (ε_term Hε_term) "Hterm".
destruct (Nat.eq_dec p (q + 1)%nat) as [-> | p_ne_q_add_1].
{ rewrite (ec_geometric_split _ _ _ _ _ HD) in HSum; last done.
pose proof (pos_INR q).
rewrite plus_INR Rdiv_diag in HSum; last (simpl; lra).
rewrite Rminus_diag Rmult_1_l Rmult_0_l Rplus_0_r in HSum.
subst.
unfold geometric, geometric_tape.
wp_pures.
iMod ec_zero as "Herr0".
wp_apply (bernoulli_success_spec_simple with "[Herr0]") as "_".
{ iApply (ec_eq with "Herr0").
rewrite S_INR plus_INR Rdiv_diag; last simpl; lra.
}
wp_pures.
iModIntro.
by iFrame.
}
assert (p < q + 1)%nat as p_lt_Sq by lia.
apply lt_INR in p_lt_Sq as p_lt_Sq'.
apply lt_INR in Hp as Hp'.
rewrite plus_INR /= in p_lt_Sq'.
simpl in Hp'.
rewrite /geometric /geometric_tape.
iRevert (D ε HD HSum) "Herr".
set (s := (p / (q + 1))%R).
set (t := ((1 - s)%R)).
assert (0 < s).
{ apply Rdiv_lt_0_compat; lra. }
assert (0 < t).
{ rewrite Rlt_0_minus -Rcomplements.Rdiv_lt_1; lra. }
assert (1 < / t).
{ unfold t, s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar; first done.
rewrite Rcomplements.Rlt_minus_l -{1}(Rplus_0_r 1).
apply Rplus_le_lt_compat; first reflexivity.
apply Rdiv_lt_0_compat; lra.
}
assert (1 < / s).
{ unfold s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar.
{ apply Rdiv_lt_0_compat; lra. }
{ rewrite -Rcomplements.Rdiv_lt_1; lra. }
}
iApply (ec_ind_amp _ (/ t) with "[] Hterm"); try done.
iModIntro.
iIntros (ε' Hε') "IH Hterm".
iIntros (D ε HD HDε) "Herr".
set (ε0 := SeriesC (λ k : nat, geometric_prob p q k * D (S k))).
assert (0 <= ε0).
{ unfold ε0.
apply SeriesC_ge_0'.
move=>k.
apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
wp_pures.
iPoseProof (ec_combine with "[Hterm Herr]") as "Hec"; first iFrame.
wp_apply (twp_bernoulli_scale _ _ _ (ε0 + ε' / t) (D 0%nat) with "Hec")
as (?) "[[-> Herr] | [-> Herr]]"; first lia.
{ by apply Rplus_le_le_0_compat; last nra. }
{ apply HD. }
{ rewrite -HDε SeriesC_first_nat; last first.
{ apply (ex_seriesC_le _ (λ k, geometric_prob p q k * L)).
{ move=>k.
split.
{ apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
{ apply Rmult_le_compat_l; last apply HD.
apply geometric_prob_pos.
lia.
}
}
apply ex_seriesC_scal_r.
eexists.
apply is_seriesC_geometric_prob.
lia.
}
unfold geometric_prob at 1.
rewrite pow_O.
erewrite SeriesC_ext; last first.
{ move=>k /=.
rewrite geometric_prob_S Rmult_assoc.
fold s t.
reflexivity.
}
rewrite SeriesC_scal_l S_INR /=.
fold ε0 s t.
rewrite (Rmult_plus_distr_r _ _ t) Rdiv_def Rmult_assoc Rinv_l; lra.
}
{
do 2 wp_pure.
iPoseProof (ec_split with "Herr") as "[Herr Hterm]"; first assumption.
{ apply Rmult_le_pos; lra. }
rewrite (Rdiv_def ε' t) (Rmult_comm ε' (/ t)).
iSpecialize ("IH" with "Hterm").
iPoseProof ("IH" $! (D ∘ S) with "[] [] Herr") as "IHH"; try done.
{ iPureIntro. move=>k. apply HD. }
wp_bind (((rec: _ _ := _) _ _)%V _)%E.
iApply tgl_wp_wand_r.
iSplitL "IHH"; first done.
iIntros (v) "(%k & -> & Herr)".
wp_pures.
iModIntro.
iExists (S k).
iFrame.
iPureIntro.
f_equal.
rewrite (Nat2Z.inj_add 1 k) //.
}
{ wp_pures.
iModIntro.
by iFrame.
}
Qed.
Lemma twp_geometric_alloc `{!erisGS Σ} (p q : nat) :
[[{ True }]]
geometric_alloc #p #q
[[{ (α : loc), RET #lbl:α; own_geometric_tape α p q [] }]].
Proof.
iIntros (Φ) "_ HΦ".
unfold geometric_alloc.
wp_apply (twp_bernoulli_alloc with "[$]") as (α) "Hα".
iApply ("HΦ" with "[$Hα]").
by iPureIntro.
Qed.
Lemma twp_geometric_presample_adv_comp `{!erisGS Σ} :
∀ (p q : nat) (α : loc) (ns : list nat)
(e : expr)
(D : nat → R) (L : R) (ε : R)
(Φ : val → iProp Σ),
(0 < p)%nat →
(p ≤ q + 1)%nat →
to_val e = None →
(∀ (n : nat), 0 <= D n <= L)%R →
SeriesC (λ k, (geometric_prob p q k * D k)%R) = ε →
own_geometric_tape α p q ns ∗
↯ ε ∗
(∀ (i : nat), ↯ (D i) ∗ own_geometric_tape α p q (ns ++ [i]) -∗ WP e [{ Φ }])
⊢ WP e [{ Φ }].
Proof.
iIntros (p q α ns e D L ε Φ Hp Hpq He HD HSum)
"((%l & Htape & %is_tl) & Herr & Hnext)".
iApply twp_err_pos; auto.
iIntros (ε_term Hε_term) "Hterm".
destruct (Nat.eq_dec p (q + 1)%nat) as [-> | p_ne_q_add_1].
{ rewrite (ec_geometric_split _ _ _ _ _ HD) in HSum; last done.
pose proof (pos_INR q).
rewrite plus_INR Rdiv_diag in HSum; last (simpl; lra).
rewrite Rminus_diag Rmult_1_l Rmult_0_l Rplus_0_r in HSum.
subst.
unfold geometric, geometric_tape.
wp_pures.
set (D' (k : fin 2) := match k with
| Fin.F1 _ => 1%R
| FS _ _ => D 0%nat
end).
wp_apply (twp_presample_bernoulli_adv_comp _ _ _ (q + 1)%nat q _ _ D' with "[$Herr $Htape Hnext]") as (k) "[Herr Htape]"; try done.
{ move=>k.
inv_fin k=>[|?] /=; first lra.
apply HD.
}
{ rewrite plus_INR /= Rdiv_diag; lra. }
inv_fin k=>[|k]/=.
{ iDestruct (ec_contradict with "Herr") as "[]". reflexivity. }
inv_fin k=>[|k]/=; last inv_fin k.
wp_apply "Hnext".
iFrame.
iPureIntro.
by apply (is_geometric_translation_snoc _ _ 0).
}
assert (p < q + 1)%nat as p_lt_Sq by lia.
apply lt_INR in p_lt_Sq as p_lt_Sq'.
apply lt_INR in Hp as Hp'.
rewrite plus_INR /= in p_lt_Sq'.
simpl in Hp'.
rewrite /geometric /geometric_tape -(app_nil_r l).
replace [] with (repeat (0%fin : fin 2) 0) by reflexivity.
iAssert (∀ i : nat,
↯ (D i) ∗
own_geometric_tape α p q (ns ++ [0 + i]%nat) -∗
WP e [{ v, Φ v }])%I with "Hnext" as "Hnext".
generalize 0%nat at 4 5 as n.
iIntros (n).
iRevert (n D ε HD HSum) "Htape Herr Hnext".
set (s := (p / (q + 1))%R).
set (t := ((1 - s)%R)).
assert (0 < s).
{ apply Rdiv_lt_0_compat; lra. }
assert (0 < t).
{ rewrite Rlt_0_minus -Rcomplements.Rdiv_lt_1; lra. }
assert (1 < / t).
{ unfold t, s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar; first done.
rewrite Rcomplements.Rlt_minus_l -{1}(Rplus_0_r 1).
apply Rplus_le_lt_compat; first reflexivity.
apply Rdiv_lt_0_compat; lra.
}
assert (1 < / s).
{ unfold s.
rewrite -{1}Rinv_1.
apply Rinv_0_lt_contravar.
{ apply Rdiv_lt_0_compat; lra. }
{ rewrite -Rcomplements.Rdiv_lt_1; lra. }
}
iApply (ec_ind_amp _ (/ t) with "[] Hterm"); try done.
iModIntro.
iIntros (ε' Hε') "IH Hterm".
iIntros (n D ε HD HDε) "Htape Herr Hnext".
set (ε0 := SeriesC (λ k : nat, geometric_prob p q k * D (S k))).
assert (0 <= ε0).
{ unfold ε0.
apply SeriesC_ge_0'.
move=>k.
apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
wp_pures.
iPoseProof (ec_combine with "[$Hterm $Herr]") as "Hec".
set (D' (k : fin 2) := match k with
| Fin.F1 _ => (ε0 + ε' / t)%R
| FS _ _ => D 0%nat
end).
wp_apply (twp_presample_bernoulli_adv_comp _ _ _ _ _ _ _ D' with "[$Hec $Htape Hnext IH]") as (k) "[Herr Htape]"; try done.
{ move=>k.
inv_fin k=>[|?] /=; last apply HD.
by apply Rplus_le_le_0_compat; last nra.
}
{ rewrite -HDε SeriesC_first_nat; last first.
{ apply (ex_seriesC_le _ (λ k, geometric_prob p q k * L)).
{ move=>k.
split.
{ apply Rmult_le_pos; last apply HD.
apply geometric_prob_pos.
lia.
}
{ apply Rmult_le_compat_l; last apply HD.
apply geometric_prob_pos.
lia.
}
}
apply ex_seriesC_scal_r.
eexists.
apply is_seriesC_geometric_prob.
lia.
}
unfold geometric_prob at 1.
rewrite pow_O.
erewrite SeriesC_ext; last first.
{ move=>k /=.
rewrite geometric_prob_S Rmult_assoc.
fold s t.
reflexivity.
}
rewrite SeriesC_scal_l /=.
fold ε0 s t.
rewrite (Rmult_plus_distr_r _ _ t) Rdiv_def Rmult_assoc Rinv_l; lra.
}
inv_fin k=>[|k] /=; last (inv_fin k=>[|k]; last inv_fin k).
{
iPoseProof (ec_split with "Herr") as "[Herr Hterm]"; first assumption.
{ apply Rmult_le_pos; lra. }
rewrite (Rdiv_def ε' t) (Rmult_comm ε' (/ t)) -app_assoc -repeat_cons.
iSpecialize ("IH" with "Hterm").
wp_apply ("IH" $! (S n) (D ∘ S) with "[] [] Htape Herr") as (k) "[Herr Htape]"; try done.
{ iPureIntro. move=>k. apply HD. }
wp_apply "Hnext".
iFrame.
rewrite Nat.add_succ_r //.
}
{ wp_apply "Hnext".
iFrame.
iPureIntro.
rewrite Nat.add_0_r -app_assoc.
by apply is_geometric_translation_snoc.
}
Qed.
Lemma twp_geometric_tape `{!erisGS Σ} :
∀ (p q : nat) (α : loc) (n : nat)
(ns : list nat) (Φ : val → iProp Σ),
own_geometric_tape α p q (n::ns) -∗
(own_geometric_tape α p q ns -∗ Φ #n) -∗
WP geometric_tape #lbl:α #p #q [{ Φ }].
Proof.
iIntros (p q α n).
iInduction (n) as [|n] "IH".
- iIntros (ns Φ) "(%l & Htape & %is_tl) HΦ".
destruct is_tl as (suf & -> & is_tl).
simpl.
unfold geometric_tape.
wp_pures.
wp_apply (twp_bernoulli_tape with "Htape") as "Htape".
wp_pures.
iModIntro.
by iApply ("HΦ" with "[$Htape]").
- iIntros (ns Φ) "(%l & Htape & %is_tl) HΦ".
destruct is_tl as (suf & -> & is_tl).
simpl.
unfold geometric_tape.
wp_pures.
wp_apply (twp_bernoulli_tape with "Htape") as "Htape".
do 2 wp_pure.
fold geometric_tape.
wp_apply ("IH" with "[$Htape]") as "Htape".
{ iPureIntro.
by eexists.
}
wp_pures.
rewrite -(Nat2Z.inj_add 1 n) /=.
iModIntro.
iApply ("HΦ" with "Htape").
Qed.
#[global] Instance geometric_of_bernoulli : geometric_spec geometric_tape geometric_alloc :=
GeometricSpec _ _
(@twp_geometric_split)
(@own_geometric_tape)
(@twp_geometric_alloc)
(@twp_geometric_tape)
(@twp_geometric_presample_adv_comp)
.
End Geometric.