Theory Tracking_Randomized_Algorithm

section ‹Tracking Randomized Algorithms\label{sec:tracking_randomized_algorithm}›

text ‹This section introduces the @{term "track_random_bits"} monad morphism, which converts a
randomized algorithm to one that tracks the number of used coin-flips. The resulting algorithm
can still be executed. This morphism is useful for testing and debugging. For the verification
of coin-flip usage, the morphism @{term "tspmf_of_ra"} introduced in
Section~\ref{sec:tracking_spmfs} is more useful.›

theory Tracking_Randomized_Algorithm
  imports Randomized_Algorithm
begin

definition track_random_bits :: "'a random_alg_int  ('a × nat) random_alg_int"
  where "track_random_bits f bs =
    do {
      (r,bs')  f bs;
      n  consumed_bits f bs;
      Some ((r,n),bs')
    }"

lemma track_random_bits_Some_iff:
  assumes "track_random_bits f bs  None"
  shows "f bs  None"
  using assms unfolding track_random_bits_def by (cases "f bs", auto)

lemma track_random_bits_alt:
  assumes "wf_random f"
  shows "track_random_bits f bs =
    map_option (λp. ((eval_rm f p, length p), cdrop (length p) bs)) (consumed_prefix f bs)"
proof (cases "consumed_prefix f bs")
  case None
  hence "f bs = None"
    by (subst wf_random_alt[OF assms(1)]) simp
  then show ?thesis
    unfolding track_random_bits_def None by simp
next
  case (Some a)
  hence "f bs = Some (eval_rm f a, cdrop (length a) bs)"
    by (subst wf_random_alt[OF assms(1)]) simp
  then show ?thesis
    unfolding track_random_bits_def Some consumed_bits_def by simp
qed

lemma track_rb_coin:
  "track_random_bits coin_rai = coin_rai  (λb. return_rai (b,1))" (is "?L = ?R")
proof (rule ext)
  fix bs :: "coin_stream"
  have "wf_on_prefix coin_rai [chd bs] (chd bs)"
    unfolding wf_on_prefix_def coin_rai_def by simp
  moreover have "cprefix [chd bs] bs"
    unfolding cprefix_def by simp
  ultimately have "{p  ptree_rm (coin_rai). cprefix p bs} = {[chd bs]}"
    by (intro prefixes_singleton) (auto simp:ptree_rm_def)
  hence "consumed_prefix (coin_rai) bs = Some [chd bs]"
    unfolding consumed_prefix_def by simp
  hence "consumed_bits (coin_rai) bs = Some 1"
    unfolding consumed_bits_def by simp
  thus "?L bs = ?R bs"
    unfolding track_random_bits_def bind_rai_def
    by (simp add:coin_rai_def return_rai_def)
qed

lemma track_rb_return: "track_random_bits (return_rai x) = return_rai (x,0)" (is "?L = ?R")
proof (rule ext)
  fix bs :: "coin_stream"
  have "wf_on_prefix (return_rai x) [] x"
    unfolding wf_on_prefix_def return_rai_def by simp
  moreover have "cprefix [] bs"
    unfolding cprefix_def by simp
  ultimately have "{p  ptree_rm (return_rai x). cprefix p bs} = {[]}"
    by (intro prefixes_singleton) (auto simp:ptree_rm_def)
  hence "consumed_prefix (return_rai x) bs = Some []"
    unfolding consumed_prefix_def by simp
  hence "consumed_bits (return_rai x) bs = Some 0"
    unfolding consumed_bits_def by simp
  thus "?L bs = ?R bs"
    unfolding track_random_bits_def by (simp add:return_rai_def)
qed

lemma consumed_prefix_imp_wf:
  assumes "consumed_prefix m bs = Some p"
  shows "wf_on_prefix m p (eval_rm m p)"
proof -
  have "p  ptree_rm m"
    using assms unfolding consumed_prefix_def the_elem_opt_Some_iff[OF prefixes_at_most_one] by blast
  then obtain r where "wf_on_prefix m p r"
    unfolding ptree_rm_def by auto
  thus ?thesis
    unfolding wf_on_prefix_def eval_rm_def by simp
qed

lemma consumed_prefix_imp_prefix:
  assumes "consumed_prefix m bs = Some p"
  shows "cprefix p bs"
  using assms unfolding consumed_prefix_def the_elem_opt_Some_iff[OF prefixes_at_most_one] by blast

lemma consumed_prefix_bindI:
  assumes "consumed_prefix m bs = Some p"
  assumes "consumed_prefix (f (eval_rm m p)) (cdrop (length p) bs) = Some q"
  shows "consumed_prefix (m  f) bs = Some (p@q)"
proof -
  define r where "r = eval_rm m p"

  have 0: "wf_on_prefix m p r"
    unfolding r_def using consumed_prefix_imp_wf[OF assms(1)] by simp

  have "consumed_prefix (f r) (cdrop (length p) bs) = Some q"
    using assms(2) unfolding r_def by simp
  hence 1: "wf_on_prefix (f r) q (eval_rm (f r) q)"
    using consumed_prefix_imp_wf by auto
  have "wf_on_prefix (m  f) (p@q) (eval_rm (f r) q)"
    by (intro wf_on_prefix_bindI[where r="r"] 0 1)
  hence "p@q  ptree_rm (m  f)"
    unfolding ptree_rm_def by auto
  moreover have "cprefix p bs" "cprefix q (cdrop (length p) bs)"
    using consumed_prefix_imp_prefix assms by auto
  hence "cprefix (p@q) bs"
    unfolding cprefix_def by (metis length_append ctake_add)
  ultimately have "{p  ptree_rm (m  f). cprefix p bs} = {p@q}"
    by (intro prefixes_singleton) auto
  thus ?thesis
    unfolding consumed_prefix_def by simp
qed

lemma track_rb_bind:
  assumes "wf_random m"
  assumes "x. x  range_rm m  wf_random (f x)"
  shows "track_random_bits (m  f) = track_random_bits m 
  (λ(r,n). track_random_bits (f r)  (λ(r',m). return_rai (r',n+m)))" (is "?L = ?R")
proof (rule ext)
  fix bs :: "coin_stream"
  have wf_bind: "wf_random (m  f)"
    by (intro wf_bind assms)

  consider (a) "m bs = None" | (b) "m bs  None  (m  f) bs = None" | (c) "(m  f) bs  None"
    by blast
  then show "?L bs = ?R bs"
  proof (cases)
    case a
    thus ?thesis
      unfolding track_random_bits_def bind_rai_def a by simp
  next
    case b
    then obtain r bs' where 0:"m bs = Some (r,bs')" by auto
    have 1:"(f r) bs' = None" using b unfolding bind_rai_def 0 by simp
    then show ?thesis  unfolding track_random_bits_def bind_rai_def 0 by simp
  next
    case c
    have "(m  f) bs  = None" if "m bs = None"
      using that unfolding bind_rai_def by simp
    hence "m bs  None" using c by blast
    then obtain p where 0:
      "m bs = Some (eval_rm m p, cdrop (length p) bs)" "consumed_prefix m bs = Some p"
      using wf_random_alt[OF assms(1)] by auto
    define bs' where "bs' = cdrop (length p) bs"
    define r where "r = eval_rm m p"
    have 1: "m bs = Some (r, bs')" unfolding 0 r_def bs'_def by simp
    hence "r  range_rm m" using 1 in_range_rmI by metis
    hence wf: "wf_random (f r)" by (intro assms(2))
    have "f r bs'  None" using c 1 unfolding bind_rai_def by force
    then obtain q where 2:
      "f r bs' = Some (eval_rm (f r) q, cdrop (length q) bs')" "consumed_prefix (f r) bs' = Some q"
      using wf_random_alt[OF wf] by auto

    hence 3:"consumed_prefix (m  f) bs = Some (p@q)"
      unfolding r_def bs'_def by (intro consumed_prefix_bindI 0) auto

    have "track_random_bits m bs = Some ((r, length p), bs')"
      unfolding track_random_bits_alt[OF assms(1)] bind_rai_def 0 bs'_def r_def by simp
    moreover have "track_random_bits (f r) bs' =
      Some ((eval_rm (f r) q, length q), cdrop (length q) bs')"
      unfolding track_random_bits_alt[OF wf] 2 by simp
    moreover have "wf_on_prefix m p r"
      unfolding r_def by (intro consumed_prefix_imp_wf[OF 0(2)])
    hence "eval_rm (f r) q = eval_rm (m  f) (p@q)"
      unfolding eval_rm_def bind_rai_def wf_on_prefix_def by simp
    ultimately have
      "?R bs = Some ((eval_rm (m  f) (p@q), length p+length q), cdrop (length p+length q) bs)"
      unfolding bind_rai_def return_rai_def bs'_def by simp
    also have "... = ?L bs"
      unfolding track_random_bits_alt[OF wf_bind] 3 by simp
    finally show ?thesis by simp
  qed
qed

lemma track_random_bits_mono:
  assumes "wf_random f" "wf_random g"
  assumes "ord_rai f g"
  shows "ord_rai (track_random_bits f) (track_random_bits g)"
proof -
  have "track_random_bits f bs = track_random_bits g bs"
    if "track_random_bits f bs  None" for bs
  proof -
    have "f bs  None" using that track_random_bits_Some_iff by simp
    then obtain r bs' where "f bs = Some (r,bs')" by auto
    then obtain p where 0:"wf_on_prefix f p r" and 2:"cprefix p bs"
      using assms(1) unfolding wf_random_def by (auto split:option.split_asm)

    have 1: "wf_on_prefix g p r"
      using wf_lub_helper[OF assms(3)] 0 by blast

    have "track_random_bits h bs = Some ((r, length p),cdrop (length p) bs)"
      if "wf_on_prefix h p r" "wf_random h" for h
    proof -
      have "p  ptree_rm h"
        using that unfolding ptree_rm_def by auto
      hence "{p  ptree_rm h. cprefix p bs} = {p}"
        using 2 by (intro prefixes_singleton) auto
      hence "consumed_prefix h bs = Some p"
        unfolding consumed_prefix_def by simp
      moreover have "eval_rm h p = r"
        using that(1) unfolding wf_on_prefix_def eval_rm_def by simp
      ultimately show  ?thesis
        unfolding track_random_bits_alt[OF that(2)] by simp
    qed

    thus ?thesis
      using 0 1 assms(1,2) by simp
  qed
  thus ?thesis
    unfolding ord_rai_def fun_ord_def flat_ord_def by blast
qed

lemma wf_track_random_bits:
  assumes "wf_random f"
  shows "wf_random (track_random_bits f)"
proof (rule wf_randomI)
  fix bs
  assume "track_random_bits f bs  None"
  hence "f bs  None" using track_random_bits_Some_iff by blast
  then obtain r bs' where "f bs = Some (r, bs')"
    by auto
  then obtain p where 0:"wf_on_prefix f p r" "cprefix p bs"
    using assms unfolding wf_random_def by (auto split:option.split_asm)
  hence "{q  ptree_rm f. cprefix q (cshift p cs)} = {p}" for cs
    by (intro prefixes_singleton) (auto simp:cprefix_def ptree_rm_def)
  hence "consumed_prefix f (cshift p cs) = Some p" for cs
    unfolding consumed_prefix_def by simp
  hence "wf_on_prefix (track_random_bits f) p (r, length p)"
    using 0 unfolding track_random_bits_def wf_on_prefix_def consumed_bits_def by simp
  thus "p r. cprefix p bs  wf_on_prefix (track_random_bits f) p r"
    using 0 by auto
qed

lemma track_random_bits_lub_rai:
  assumes "Complete_Partial_Order.chain ord_rai A"
  assumes "r. r  A  wf_random r"
  shows "track_random_bits (lub_rai A) = lub_rai (track_random_bits ` A)" (is "?L = ?R")
proof -
  have 0:"Complete_Partial_Order.chain ord_rai (track_random_bits ` A)"
    by (intro chain_imageI[OF assms(1)] track_random_bits_mono assms(2))

  have "?L bs = ?R bs" if "?L bs  None" for bs
  proof -
    have 1:"lub_rai A bs  None" using that track_random_bits_Some_iff by simp
    have "lub_rai A bs = None" if "f. f  A  f bs = None"
      using that unfolding lub_rai_def fun_lub_def flat_lub_def by auto
    then obtain f where f_in_A: "f  A" and "f bs  None"
      using 1 by blast
    hence "consumed_prefix f bs  None"
      using consumed_prefix_none_iff[OF assms(2)[OF f_in_A]] by simp
    hence 2:"track_random_bits f bs  None"
      unfolding track_random_bits_alt[OF assms(2)[OF f_in_A]] by simp
    have "ord_rai (track_random_bits f) (track_random_bits (lub_rai A))"
      by (intro track_random_bits_mono wf_lub[OF assms(1)] assms(2)
          random_alg_int_pd.lub_upper[OF assms(1)] f_in_A)
    hence "track_random_bits (lub_rai A) bs = track_random_bits f bs"
      using 2 unfolding ord_rai_def fun_ord_def flat_ord_def by metis

    moreover have "ord_rai (track_random_bits f) (lub_rai (track_random_bits ` A))"
      using f_in_A by (intro random_alg_int_pd.lub_upper[OF 0]) auto
    hence "lub_rai (track_random_bits ` A) bs = track_random_bits f bs"
      using 2 unfolding ord_rai_def fun_ord_def flat_ord_def by metis
    ultimately show ?thesis by simp
  qed
  hence "flat_ord None (?L bs) (?R bs)" for bs
    unfolding flat_ord_def by blast
  hence "ord_rai ?L ?R"
    unfolding ord_rai_def fun_ord_def by simp
  moreover have "ord_rai (track_random_bits f) (track_random_bits (lub_rai A))" if "f  A" for f
    using that assms(2) wf_lub[OF assms(1,2)]
    by (intro track_random_bits_mono random_alg_int_pd.lub_upper[OF assms(1)])
  hence "ord_rai ?R ?L"
    by (intro random_alg_int_pd.lub_least 0) auto
  ultimately show ?thesis
    using random_alg_int_pd.leq_antisym by auto
qed

lemma untrack_random_bits:
  assumes "wf_random f"
  shows "track_random_bits f  (λx. return_rai (fst x)) = f" (is "?L = ?R")
proof -
  have "?L bs = ?R bs" for bs
    unfolding track_random_bits_alt[OF assms] bind_rai_def return_rai_def
    by (subst wf_random_alt[OF assms]) (cases "consumed_prefix f bs", auto)
  thus ?thesis
    by auto
qed

lift_definition track_coin_use :: "'a random_alg  ('a × nat) random_alg"
  is track_random_bits
  by (rule wf_track_random_bits)

definition bind_tra ::
  "('a × nat) random_alg  ('a  ('b × nat) random_alg)  ('b × nat) random_alg"
  where "bind_tra m f = do {
    (r,k)  m;
    (s,l)  (f r);
    return_ra (s, k+l)
  }"

definition coin_tra :: "(bool × nat) random_alg"
  where "coin_tra = do {
    b  coin_ra;
    return_ra (b,1)
  }"

definition return_tra :: "'a  ('a × nat) random_alg"
  where "return_tra x = return_ra (x,0)"

adhoc_overloading Monad_Syntax.bind bind_tra

text ‹Monad laws:›

lemma return_bind_tra:
  "bind_tra (return_tra x) g = g x"
  unfolding bind_tra_def return_tra_def
  by (simp add:bind_return_ra return_bind_ra)

lemma bind_tra_assoc:
  "bind_tra (bind_tra f g) h = bind_tra f (λx. bind_tra (g x) h)"
  unfolding bind_tra_def
  by (simp add:bind_return_ra return_bind_ra bind_ra_assoc case_prod_beta' algebra_simps)

lemma bind_return_tra:
  "bind_tra m return_tra = m"
  unfolding bind_tra_def return_tra_def
  by (simp add:bind_return_ra return_bind_ra)

lemma track_coin_use_bind:
  fixes m :: "'a random_alg"
  fixes f :: "'a  'b random_alg"
  shows "track_coin_use (m  f) = track_coin_use m  (λr. track_coin_use (f r))"
    (is "?L = ?R")
proof -
  have "Rep_random_alg ?L = Rep_random_alg ?R"
    unfolding track_coin_use.rep_eq bind_ra.rep_eq bind_tra_def
    by (subst track_rb_bind) (simp_all add:wf_rep_rand_alg comp_def case_prod_beta'
        track_coin_use.rep_eq bind_ra.rep_eq return_ra.rep_eq)
  thus ?thesis
    using Rep_random_alg_inject by auto
qed

lemma track_coin_use_coin: "track_coin_use coin_ra = coin_tra" (is "?L = ?R")
  unfolding coin_tra_def using track_rb_coin[transferred] by metis

lemma track_coin_use_return: "track_coin_use (return_ra x) = return_tra x"  (is "?L = ?R")
  unfolding return_tra_def using track_rb_return[transferred] by metis

lemma track_coin_use_lub:
  assumes "Complete_Partial_Order.chain ord_ra A"
  shows "track_coin_use (lub_ra A) = lub_ra (track_coin_use ` A)" (is "?L = ?R")
proof -
  have 0: "Complete_Partial_Order.chain ord_rai (Rep_random_alg ` A)"
    using assms unfolding ord_ra.rep_eq Complete_Partial_Order.chain_def by auto

  have 2: "(Rep_random_alg ` track_coin_use ` A) = track_random_bits ` Rep_random_alg ` A"
    using track_coin_use.rep_eq unfolding image_image by auto

  have 1: "Complete_Partial_Order.chain ord_rai (Rep_random_alg ` track_coin_use ` A)"
    using wf_rep_rand_alg unfolding 2 by (intro chain_imageI[OF 0] track_random_bits_mono) auto

  have "Rep_random_alg ?L = track_random_bits (lub_rai (Rep_random_alg ` A))"
    using 0 unfolding track_coin_use.rep_eq lub_ra.rep_eq by simp
  also have "... = lub_rai (track_random_bits ` Rep_random_alg ` A)"
    using wf_rep_rand_alg by (intro track_random_bits_lub_rai 0) auto
  also have "... = Rep_random_alg ?R"
    using 1 unfolding lub_ra.rep_eq 2 by simp
  finally have "Rep_random_alg ?L = Rep_random_alg ?R"
    by simp
  thus ?thesis
    using Rep_random_alg_inject by auto
qed

lemma track_coin_use_mono:
  assumes "ord_ra f g"
  shows "ord_ra (track_coin_use f) (track_coin_use g)"
  using assms by transfer (rule track_random_bits_mono)

lemma bind_mono_tra_aux:
  assumes "ord_ra f1 f2" "y. ord_ra (g1 y) (g2 y)"
  shows "ord_ra (bind_tra f1 g1) (bind_tra f2 g2)"
  using assms unfolding bind_tra_def ord_ra.rep_eq bind_ra.rep_eq
  by (auto intro!:bind_rai_mono random_alg_int_pd.leq_refl
      simp:comp_def bind_ra.rep_eq case_prod_beta' return_ra.rep_eq)

lemma bind_tra_mono [partial_function_mono]:
  assumes "mono_ra B" and "y. mono_ra (C y)"
  shows "mono_ra (λf. bind_tra (B f) (λy. C y f))"
  using assms by (intro monotoneI bind_mono_tra_aux) (auto simp:monotone_def)

lemma track_coin_use_empty:
  "track_coin_use (lub_ra {}) = (lub_ra {})" (is "?L = ?R")
proof -
  have "?L = lub_ra (track_coin_use ` {})"
    by (intro track_coin_use_lub) (simp add:Complete_Partial_Order.chain_def)
  also have "... = ?R" by simp
  finally show ?thesis by simp
qed

lemma untrack_coin_use:
  "map_ra fst (track_coin_use f) = f" (is "?L = ?R")
proof -
  have "Rep_random_alg ?L = Rep_random_alg ?R"
    unfolding map_ra_def bind_ra.rep_eq track_coin_use.rep_eq comp_def return_ra.rep_eq
    by (auto intro!:untrack_random_bits simp:wf_rep_rand_alg)
  thus ?thesis
    using Rep_random_alg_inject by auto
qed

definition rel_track_coin_use :: "('a × nat) random_alg  'a random_alg  bool" where
  "rel_track_coin_use q p  q = track_coin_use p"

lemma admissible_rel_track_coin_use:
  "ccpo.admissible (prod_lub lub_ra lub_ra) (rel_prod ord_ra ord_ra) (case_prod rel_track_coin_use)"
  (is "ccpo.admissible ?lub ?ord ?P")
proof (rule ccpo.admissibleI)
  fix Y
  assume chain: "Complete_Partial_Order.chain ?ord Y"
    and Y: "Y  {}"
    and R: "(p, q)  Y. rel_track_coin_use p q"
  from R have R: "p q. (p, q)  Y  rel_track_coin_use p q" by auto
  have chain1: "Complete_Partial_Order.chain (ord_ra) (fst ` Y)"
    and chain2: "Complete_Partial_Order.chain (ord_ra) (snd ` Y)"
    using chain by(rule chain_imageI; clarsimp)+
  from Y have Y1: "fst ` Y  {}" and Y2: "snd ` Y  {}" by auto

  have "lub_ra (fst ` Y) = lub_ra (track_coin_use ` snd ` Y)"
    unfolding image_image using R
    by (intro arg_cong[of _ _ lub_ra] image_cong) (auto simp: rel_track_coin_use_def)
  also have " = track_coin_use (lub_ra (snd ` Y))"
    by (intro track_coin_use_lub[symmetric] chain2)
  finally have "rel_track_coin_use (lub_ra (fst ` Y)) (lub_ra (snd ` Y))"
    unfolding rel_track_coin_use_def .
  then show "?P (?lub Y)"
    by (simp add: prod_lub_def)
qed

lemma admissible_rel_track_coin_use_cont [cont_intro]:
  fixes ord
  shows " mcont lub ord lub_ra ord_ra f; mcont lub ord lub_ra ord_ra g 
   ccpo.admissible lub ord (λx. rel_track_coin_use (f x) (g x))"
  by (rule admissible_subst[OF admissible_rel_track_coin_use, where f="λx. (f x, g x)", simplified])
     (rule mcont_Pair)

lemma mcont_track_coin_use:
  "mcont lub_ra ord_ra lub_ra ord_ra track_coin_use"
  unfolding mcont_def monotone_def cont_def
  by (auto simp: track_coin_use_mono track_coin_use_lub)

lemmas mcont2mcont_track_coin_use = mcont_track_coin_use[THEN random_alg_pf.mcont2mcont]

context includes lifting_syntax
begin

lemma fixp_track_coin_use_parametric[transfer_rule]:
  assumes f: "x. mono_ra (λf. F f x)"
  and g: "x. mono_ra (λf. G f x)"
  and param: "((A ===> rel_track_coin_use) ===> A ===> rel_track_coin_use) F G"
  shows "(A ===> rel_track_coin_use) (random_alg_pf.fixp_fun F) (random_alg_pf.fixp_fun G)"
  using f g
proof(rule parallel_fixp_induct_1_1[OF
      random_alg_pfd random_alg_pfd _ _ reflexive reflexive,
        where P="(A ===> rel_track_coin_use)"])
  show "ccpo.admissible (prod_lub (fun_lub lub_ra) (fun_lub lub_ra))
        (rel_prod (fun_ord ord_ra) (fun_ord ord_ra))
        (λx. (A ===> rel_track_coin_use) (fst x) (snd x))"
    unfolding rel_fun_def
    by(rule admissible_all admissible_imp cont_intro)+
  have 0:"track_coin_use (lub_ra {}) = lub_ra {}"
    using track_coin_use_lub[where A="{}"]
    by (simp add:Complete_Partial_Order.chain_def)
  show "(A ===> rel_track_coin_use) (λ_. lub_ra {}) (λ_. lub_ra {})"
    by (auto simp: rel_fun_def rel_track_coin_use_def 0)
  show "(A ===> rel_track_coin_use) (F f) (G g)" if "(A ===> rel_track_coin_use) f g" for f g
    using that by(rule rel_funD[OF param])
qed

lemma return_ra_tranfer[transfer_rule]: "((=) ===> rel_track_coin_use) return_tra return_ra"
  unfolding rel_fun_def rel_track_coin_use_def track_coin_use_return by simp

lemma bind_ra_tranfer[transfer_rule]:
  "(rel_track_coin_use ===> ((=) ===> rel_track_coin_use) ===> rel_track_coin_use) bind_tra bind_ra"
  unfolding rel_fun_def rel_track_coin_use_def track_coin_use_bind by simp presburger

lemma coin_ra_tranfer[transfer_rule]:
  "rel_track_coin_use coin_tra coin_ra"
  unfolding rel_fun_def rel_track_coin_use_def track_coin_use_coin by simp

end

end