clutch.eris.examples.rec_toss
From clutch.eris Require Export eris error_rules.
From Coquelicot Require Import Series.
From Stdlib Require Import Lra.
Section toss_rec.
Context `{!erisGS Σ}.
(* Taped version in case we need it *)
Definition toss_rec_tape : expr :=
rec: "toss" "a" "α" :=
let: "x" := rand("α") #2 in
if: ("x" ≤ #0) (* or just "x" = 0 *)
then #()
else "toss" #() "α" ;; "toss" #() "α".
Definition prog_tape : expr :=
let: "α" := alloc #1 in
toss_rec_tape #() "α".
Definition toss_rec : val :=
rec: "toss" "a" :=
let: "x" := rand #2 in
if: ("x" ≤ #0) (* or just "x" = 0 *)
then #()
else "toss" #() ;; "toss" #().
Definition prog : expr :=
toss_rec #().
(* Equivalent random walk:
* x = 1
* while x > 0:
* with prob 1/3: x := x - 1
* with prob 2/3: x := x + 1
*)
(* make termination probability of `toss` to be `x`
* then we have `x = 1/3 + 2/3 x^2, which has 2 roots: 1, 1/2
*)
(* To have enough error credits for the second recursive call to toss, we need to amplify
* the error credits from the first recursive call. If we can normalize the thin air credit
* by the termination probability, then we have enough credits.
*)
(* in general: { ↯(y) * ↯(r) } prog () { ↯( r * 1/(1-y) ) } *)
(* where `y` is the non-termination probability solution of `prog` *)
(* We are not able to prove this... *)
(*
* In recursive second branch, we have `3/4 + (1/2) * r` credits
* (we need to spend `2/3 * r` credit for the first branch)
* If we rearrange the error credits to `1/2 + ((1/4 + r/2))`, then
* after the first recursive call, we get `1/2 + r` back
*)
Lemma toss_rec_spec_aux r E:
⊢ [[{ ↯ (1/2) ∗ ↯(r) }]] prog @ E [[{ (v : val), RET v; ↯(r * 2) }]].
Proof.
iIntros.
rewrite /prog /toss_rec.
iIntros "!>".
(* merge the error credits together *)
iIntros (Φ) "[Herr Herr1] Hwp".
iApply (twp_err_pos).
{ done. }
iIntros (ε) "%Hεge0 Hε".
(* We want to use the toss_rec_spec_aux for induction *)
iAssert (
(□
∀ r Φ,
↯ (1 / 2) -∗
↯ r -∗
(∀ v : val, ↯ (r * 2) -∗ Φ v) -∗
WP (rec: "toss" "a" :=
let: "x" := rand #2 in if: "x" ≤ #0 then #() else "toss" #();; "toss" #())%V
#()
@ E
[{ v, Φ v }]) %I
) with "[Herr Herr1 Hε]" as "#Hrec".
{
iApply (ec_ind_amp ε (3/ 2) ) .
* done.
* real_solver.
* iIntros "!>".
iIntros (ε') "He #He1 He'".
iIntros "!>". (* We lost ε' and we can't do induction *)
clear r. clear Φ.
iIntros (r Φ) "He Hr HΦ".
give_up.
* done.
}
iApply ("Hrec" with "Herr Herr1 Hwp").
Admitted.
(* Caveat: this means prog non-termination is upper bounded by 1/2 *)
Lemma toss_rec_spec E :
⊢ ↯ (1 / 2) -∗ WP prog @ E [{ _, True%I }].
Proof.
iIntros "Herr".
wp_rec.
set (ε2 := λ n : fin (S 2), if (fin_to_nat n <=? 0) then 0%R else (3 / 4)%R).
wp_apply (twp_rand_exp_fin1 _ _ _ _ ε2 with "[$]").
{ intros; rewrite /ε2; case_match; lra. }
{ rewrite SeriesC_finite_foldr /ε2. simpl. lra. }
iIntros (n) "Herr".
wp_pures.
case_bool_decide; wp_pures; first done.
- wp_apply (toss_rec_spec_aux (1/4)%R _ with "[Herr]").
* rewrite /ε2.
assert (n <=? 0 = false) as ->.
{ apply Nat.leb_gt. lia. }
iApply ec_split; [lra|lra|]. replace (3/4)%R with (1/2 + 1/4)%R by lra. iFrame.
* iIntros (v) "Herr".
wp_pures.
wp_apply (toss_rec_spec_aux (0)%R _ with "[Herr]").
-- replace (1/4*2)%R with (1/2)%R by lra. iApply ec_split; [lra|lra|]. replace (1/2)%R with (1/2 + 0)%R by lra. iFrame.
replace (1/2+0+0)%R with (1/2+0)%R by lra. iFrame.
-- iIntros (v') "Herr". done.
Qed.
End toss_rec.
From Coquelicot Require Import Series.
From Stdlib Require Import Lra.
Section toss_rec.
Context `{!erisGS Σ}.
(* Taped version in case we need it *)
Definition toss_rec_tape : expr :=
rec: "toss" "a" "α" :=
let: "x" := rand("α") #2 in
if: ("x" ≤ #0) (* or just "x" = 0 *)
then #()
else "toss" #() "α" ;; "toss" #() "α".
Definition prog_tape : expr :=
let: "α" := alloc #1 in
toss_rec_tape #() "α".
Definition toss_rec : val :=
rec: "toss" "a" :=
let: "x" := rand #2 in
if: ("x" ≤ #0) (* or just "x" = 0 *)
then #()
else "toss" #() ;; "toss" #().
Definition prog : expr :=
toss_rec #().
(* Equivalent random walk:
* x = 1
* while x > 0:
* with prob 1/3: x := x - 1
* with prob 2/3: x := x + 1
*)
(* make termination probability of `toss` to be `x`
* then we have `x = 1/3 + 2/3 x^2, which has 2 roots: 1, 1/2
*)
(* To have enough error credits for the second recursive call to toss, we need to amplify
* the error credits from the first recursive call. If we can normalize the thin air credit
* by the termination probability, then we have enough credits.
*)
(* in general: { ↯(y) * ↯(r) } prog () { ↯( r * 1/(1-y) ) } *)
(* where `y` is the non-termination probability solution of `prog` *)
(* We are not able to prove this... *)
(*
* In recursive second branch, we have `3/4 + (1/2) * r` credits
* (we need to spend `2/3 * r` credit for the first branch)
* If we rearrange the error credits to `1/2 + ((1/4 + r/2))`, then
* after the first recursive call, we get `1/2 + r` back
*)
Lemma toss_rec_spec_aux r E:
⊢ [[{ ↯ (1/2) ∗ ↯(r) }]] prog @ E [[{ (v : val), RET v; ↯(r * 2) }]].
Proof.
iIntros.
rewrite /prog /toss_rec.
iIntros "!>".
(* merge the error credits together *)
iIntros (Φ) "[Herr Herr1] Hwp".
iApply (twp_err_pos).
{ done. }
iIntros (ε) "%Hεge0 Hε".
(* We want to use the toss_rec_spec_aux for induction *)
iAssert (
(□
∀ r Φ,
↯ (1 / 2) -∗
↯ r -∗
(∀ v : val, ↯ (r * 2) -∗ Φ v) -∗
WP (rec: "toss" "a" :=
let: "x" := rand #2 in if: "x" ≤ #0 then #() else "toss" #();; "toss" #())%V
#()
@ E
[{ v, Φ v }]) %I
) with "[Herr Herr1 Hε]" as "#Hrec".
{
iApply (ec_ind_amp ε (3/ 2) ) .
* done.
* real_solver.
* iIntros "!>".
iIntros (ε') "He #He1 He'".
iIntros "!>". (* We lost ε' and we can't do induction *)
clear r. clear Φ.
iIntros (r Φ) "He Hr HΦ".
give_up.
* done.
}
iApply ("Hrec" with "Herr Herr1 Hwp").
Admitted.
(* Caveat: this means prog non-termination is upper bounded by 1/2 *)
Lemma toss_rec_spec E :
⊢ ↯ (1 / 2) -∗ WP prog @ E [{ _, True%I }].
Proof.
iIntros "Herr".
wp_rec.
set (ε2 := λ n : fin (S 2), if (fin_to_nat n <=? 0) then 0%R else (3 / 4)%R).
wp_apply (twp_rand_exp_fin1 _ _ _ _ ε2 with "[$]").
{ intros; rewrite /ε2; case_match; lra. }
{ rewrite SeriesC_finite_foldr /ε2. simpl. lra. }
iIntros (n) "Herr".
wp_pures.
case_bool_decide; wp_pures; first done.
- wp_apply (toss_rec_spec_aux (1/4)%R _ with "[Herr]").
* rewrite /ε2.
assert (n <=? 0 = false) as ->.
{ apply Nat.leb_gt. lia. }
iApply ec_split; [lra|lra|]. replace (3/4)%R with (1/2 + 1/4)%R by lra. iFrame.
* iIntros (v) "Herr".
wp_pures.
wp_apply (toss_rec_spec_aux (0)%R _ with "[Herr]").
-- replace (1/4*2)%R with (1/2)%R by lra. iApply ec_split; [lra|lra|]. replace (1/2)%R with (1/2 + 0)%R by lra. iFrame.
replace (1/2+0+0)%R with (1/2+0)%R by lra. iFrame.
-- iIntros (v') "Herr". done.
Qed.
End toss_rec.