Theory WellDefined

(*
 * Copyright (C) 2014 NICTA
 * All rights reserved.
 *)

(* Author: David Cock - David.Cock@nicta.com.au *)

section ‹Well-Defined Programs.›

theory WellDefined imports
  Healthiness
  Sublinearity
  LoopInduction
begin

text ‹The definition of a well-defined program collects the various notions of healthiness and
well-behavedness that we have so far established: healthiness of the strict and liberal
transformers, continuity and sublinearity of the strict transformers, and two new properties.
These are that the strict transformer always lies below the liberal one (i.e. that it is at least
as \emph{strict}, recalling the standard embedding of a predicate), and that expectation
conjunction is distributed between then in a particular manner, which will be crucial in
establishing the loop rules.›

subsection ‹Strict Implies Liberal›

text ‹This establishes the first connection between the strict and
  liberal interpretations (@{term wp} and @{term wlp}).›

definition
  wp_under_wlp :: "'s prog  bool"
where
  "wp_under_wlp prog  P. unitary P  wp prog P  wlp prog P"

lemma wp_under_wlpI[intro]:
  " P. unitary P  wp prog P  wlp prog P   wp_under_wlp prog"
  unfolding wp_under_wlp_def by(simp)

lemma wp_under_wlpD[dest]:
  " wp_under_wlp prog; unitary P   wp prog P  wlp prog P"
  unfolding wp_under_wlp_def by(simp)

lemma wp_under_le_trans:
  "wp_under_wlp a  le_utrans (wp a) (wlp a)"
  by(blast)

lemma wp_under_wlp_Abort:
  "wp_under_wlp Abort"
  by(rule wp_under_wlpI, unfold wp_eval, auto)

lemma wp_under_wlp_Skip:
  "wp_under_wlp Skip"
  by(rule wp_under_wlpI, unfold wp_eval, blast)

lemma wp_under_wlp_Apply:
  "wp_under_wlp (Apply f)"
  by(auto simp:wp_eval)

lemma wp_under_wlp_Seq:
  assumes h_wlp_a: "nearly_healthy (wlp a)"
      and h_wp_b:  "healthy (wp b)"
      and h_wlp_b: "nearly_healthy (wlp b)"
      and wp_u_a:  "wp_under_wlp a"
      and wp_u_b:  "wp_under_wlp b"
  shows "wp_under_wlp (a ;; b)"
proof(rule wp_under_wlpI, unfold wp_eval o_def)
  fix P::"'a  real" assume uP: "unitary P"
  with h_wp_b have "unitary (wp b P)" by(blast)
  with wp_u_a have "wp a (wp b P)  wlp a (wp b P)" by(auto)
  also {
    from wp_u_b and uP have "wp b P  wlp b P" by(blast)
    with h_wlp_a and h_wlp_b and h_wp_b and uP
    have "wlp a (wp b P)  wlp a (wlp b P)"
      by(blast intro:nearly_healthy_monoD[OF h_wlp_a])
  }
  finally show "wp a (wp b P)  wlp a (wlp b P)" .
qed

lemma wp_under_wlp_PC:
  assumes h_wp_a:  "healthy (wp a)"
      and h_wlp_a: "nearly_healthy (wlp a)"
      and h_wp_b:  "healthy (wp b)"
      and h_wlp_b: "nearly_healthy (wlp b)"
      and wp_u_a:  "wp_under_wlp a"
      and wp_u_b:  "wp_under_wlp b"
      and uP:      "unitary P"
  shows "wp_under_wlp (aP⇙⊕ b)"
proof(rule wp_under_wlpI, unfold wp_eval, rule le_funI)
  fix Q::"'a  real" and s
  assume uQ: "unitary Q"
  from uP have "P s  1" by(blast)
  hence "0  1 - P s" by(simp)
  moreover
  from uQ and wp_u_b have "wp b Q s  wlp b Q s" by(blast)
  ultimately
  have "(1 - P s) * wp b Q s  (1 - P s) * wlp b Q s"
    by(blast intro:mult_left_mono)

  moreover {
    from uQ and wp_u_a have "wp a Q s  wlp a Q s" by(blast)
    with uP have "P s * wp a Q s  P s * wlp a Q s"
      by(blast intro:mult_left_mono)
  }

  ultimately
  show "P s * wp  a Q s + (1 - P s) * wp  b Q s 
        P s * wlp a Q s + (1 - P s) * wlp b Q s"
    by(blast intro: add_mono)
qed

lemma wp_under_wlp_DC:
  assumes wp_u_a:  "wp_under_wlp a"
      and wp_u_b:  "wp_under_wlp b"
  shows "wp_under_wlp (a  b)"
proof(rule wp_under_wlpI, unfold wp_eval, rule le_funI)
  fix Q::"'a  real" and s
  assume uQ: "unitary Q"

  from wp_u_a uQ have "wp a Q s  wlp a Q s" by(blast)
  moreover
  from wp_u_b uQ have "wp b Q s  wlp b Q s" by(blast)
  ultimately
  show "min (wp a Q s) (wp b Q s)  min (wlp a Q s) (wlp b Q s)"
    by(auto)
qed

lemma wp_under_wlp_SetPC:
  assumes wp_u_f:  "s a. a  supp (P s)  wp_under_wlp (f a)"
      and nP:      "s a. a  supp (P s)  0  P s a"
  shows "wp_under_wlp (SetPC f P)"
proof(rule wp_under_wlpI, unfold wp_eval, rule le_funI)
  fix Q::"'a  real" and s
  assume uQ: "unitary Q"

  from wp_u_f uQ nP
  show "(asupp (P s). P s a * wp (f a) Q s)  (asupp (P s). P s a * wlp (f a) Q s)"
    by(auto intro!:sum_mono mult_left_mono)
qed

lemma wp_under_wlp_SetDC:
  assumes wp_u_f:  "s a. a  S s  wp_under_wlp (f a)"
      and hf:      "s a. a  S s  healthy (wp (f a))"
      and nS:      "s. S s  {}"
  shows "wp_under_wlp (SetDC f S)"
proof(rule wp_under_wlpI, rule le_funI, unfold wp_eval)
  fix Q::"'a  real" and s
  assume uQ: "unitary Q"

  show "Inf ((λa. wp (f a) Q s) ` S s)  Inf ((λa. wlp (f a) Q s) ` S s)"
  proof(rule cInf_mono)
    from nS show "(λa. wlp (f a) Q s) ` S s  {}" by(blast)

    fix x assume xin: "x  (λa. wlp (f a) Q s) ` S s"
    then obtain a where ain: "a  S s" and xrw: "x = wlp (f a) Q s"
      by(blast)
    with wp_u_f uQ
    have "wp (f a) Q s  wlp (f a) Q s" by(blast)
    moreover from ain have "wp (f a) Q s  (λa. wp (f a) Q s) ` S s"
      by(blast)
    ultimately show "y (λa. wp (f a) Q s) ` S s. y  x"
      by(auto simp:xrw)

  next
    fix y assume yin: "y  (λa. wp (f a) Q s) ` S s"
    then obtain a where ain: "a  S s" and yrw: "y = wp (f a) Q s"
      by(blast)
    with hf uQ have "unitary (wp (f a) Q)" by(auto)
    with yrw show "0  y" by(auto)
  qed
qed

lemma wp_under_wlp_Embed:
  "wp_under_wlp (Embed t)"
  by(rule wp_under_wlpI, unfold wp_eval, blast)

lemma wp_under_wlp_loop:
  fixes body::"'s prog"
  assumes hwp: "healthy (wp body)"
      and hwlp: "nearly_healthy (wlp body)"
      and wp_under: "wp_under_wlp body"
  shows "wp_under_wlp (do G  body od)"
proof(rule wp_under_wlpI)
  fix P::"'s expect"
  assume uP: "unitary P" hence sP: "sound P" by(auto)

  let "?X Q s" = "«G» s * wp body Q s + «𝒩 G» s * P s"
  let "?Y Q s" = "«G» s * wlp body Q s + «𝒩 G» s * P s"

  show "wp (do G  body od) P  wlp (do G  body od) P"
  proof(simp add:hwp hwlp sP uP wp_Loop1 wlp_Loop1, rule gfp_exp_upperbound)
    thm lfp_loop_fp
    from hwp sP have "lfp_exp ?X = ?X (lfp_exp ?X)"
      by(rule lfp_wp_loop_unfold)
    hence "lfp_exp ?X  ?X (lfp_exp ?X)" by(simp)
    also {
      from hwp uP have "wp body (lfp_exp ?X)  wlp body (lfp_exp ?X)"
        by(auto intro:wp_under_wlpD[OF wp_under] lfp_loop_unitary)
      hence "?X (lfp_exp ?X)  ?Y (lfp_exp ?X)"
        by(auto intro:add_mono mult_left_mono)
    }
    finally show "lfp_exp ?X  ?Y (lfp_exp ?X)" .
    from hwp uP show "unitary (lfp_exp ?X)"
      by(auto intro:lfp_loop_unitary)
  qed
qed

lemma wp_under_wlp_repeat:
  " healthy (wp a); nearly_healthy (wlp a); wp_under_wlp a  
   wp_under_wlp (repeat n a)"
  by(induct n, auto intro!:wp_under_wlp_Skip wp_under_wlp_Seq healthy_intros)

lemma wp_under_wlp_Bind:
  " s. wp_under_wlp (a (f s))   wp_under_wlp (Bind f a)"
  unfolding wp_under_wlp_def by(auto simp:wp_eval)

lemmas wp_under_wlp_intros =
  wp_under_wlp_Abort wp_under_wlp_Skip
  wp_under_wlp_Apply wp_under_wlp_Seq
  wp_under_wlp_PC    wp_under_wlp_DC
  wp_under_wlp_SetPC wp_under_wlp_SetDC
  wp_under_wlp_Embed wp_under_wlp_loop
  wp_under_wlp_repeat wp_under_wlp_Bind

subsection ‹Sub-Distributivity of Conjunction›

definition
  sub_distrib_pconj :: "'s prog  bool"
where
  "sub_distrib_pconj prog 
   P Q. unitary P  unitary Q 
         wlp prog P && wp prog Q  wp prog (P && Q)"

lemma sub_distrib_pconjI[intro]:
  "P Q.  unitary P; unitary Q    wlp prog P && wp prog Q  wp prog (P && Q)  
    sub_distrib_pconj prog"
  unfolding sub_distrib_pconj_def by(simp)

lemma sub_distrib_pconjD[dest]:
  "P Q.  sub_distrib_pconj prog; unitary P; unitary Q  
   wlp prog P && wp prog Q  wp prog (P && Q)"
  unfolding sub_distrib_pconj_def by(simp)

lemma sdp_Abort:
  "sub_distrib_pconj Abort"
  by(rule sub_distrib_pconjI, unfold wp_eval, auto intro:exp_conj_rzero)

lemma sdp_Skip:
  "sub_distrib_pconj Skip"
  by(rule sub_distrib_pconjI, simp add:wp_eval)

lemma sdp_Seq:
  fixes a and b
  assumes sdp_a:   "sub_distrib_pconj a"
      and sdp_b:   "sub_distrib_pconj b"
      and h_wp_a:  "healthy (wp a)"
      and h_wp_b:  "healthy (wp b)"
      and h_wlp_b: "nearly_healthy (wlp b)"
  shows "sub_distrib_pconj (a ;; b)"
proof(rule sub_distrib_pconjI, unfold wp_eval o_def)
  fix P::"'a  real" and Q::"'a  real"
  assume uP: "unitary P" and uQ: "unitary Q"

  with h_wp_b and h_wlp_b
  have "wlp a (wlp b P) && wp a (wp b Q)  wp a (wlp b P && wp b Q)"
    by(blast intro!:sub_distrib_pconjD[OF sdp_a])
  also {
    from sdp_b and uP and uQ
    have "wlp b P && wp b Q  wp b (P && Q)" by(blast)
    with h_wp_a h_wp_b h_wlp_b uP uQ
    have "wp a (wlp b P && wp b Q)  wp a (wp b (P && Q))"
      by(blast intro!:mono_transD[OF healthy_monoD, OF h_wp_a] unitary_sound
                      unitary_intros sound_intros)
  }
  finally show "wlp a (wlp b P) && wp a (wp b Q)  wp a (wp b (P && Q))" .
qed

lemma sdp_Apply:
  "sub_distrib_pconj (Apply f)"
  by(rule sub_distrib_pconjI, simp add:wp_eval)

lemma sdp_DC:
  fixes a::"'s prog" and b
  assumes sdp_a:   "sub_distrib_pconj a"
      and sdp_b:   "sub_distrib_pconj b"
      and h_wp_a:  "healthy (wp a)"
      and h_wp_b:  "healthy (wp b)"
      and h_wlp_b: "nearly_healthy (wlp b)"
  shows "sub_distrib_pconj (a  b)"
proof(rule sub_distrib_pconjI, unfold wp_eval, rule le_funI)
  fix P::"'s  real" and Q::"'s  real" and s::'s
  assume uP: "unitary P" and uQ: "unitary Q"

  have "((λs. min (wlp a P s) (wlp b P s)) &&
         (λs. min (wp a Q s) (wp b Q s))) s 
        min (wlp a P s .& wp a Q s) (wlp b P s .& wp b Q s)"
    unfolding exp_conj_def by(rule min_pconj)
  also {
    have "(λs. wlp a P s .& wp a Q s) = wlp a P && wp a Q"
      by(simp add:exp_conj_def)
    also from sdp_a uP uQ have "...  wp a (P && Q)"
      by(blast dest:sub_distrib_pconjD)
    finally have "wlp a P s .& wp a Q s  wp a (P && Q) s"
      by(rule le_funD)
    moreover {
      have "(λs. wlp b P s .& wp b Q s) = wlp b P && wp b Q"
        by(simp add:exp_conj_def)
    also from sdp_b uP uQ have "...  wp b (P && Q)"
      by(blast)
    finally have "wlp b P s .& wp b Q s  wp b (P && Q) s"
      by(rule le_funD)
    }
    ultimately
    have "min (wlp a P s .& wp a Q s) (wlp b P s .& wp b Q s) 
          min (wp a (P && Q) s) (wp b (P && Q) s)" by(auto)
  }
  finally
  show "((λs. min (wlp a P s) (wlp b P s)) &&
         (λs. min (wp a Q s) (wp b Q s))) s 
        min (wp a (P && Q) s) (wp b (P && Q) s)" .
qed

lemma sdp_PC:
  fixes a::"'s prog" and b
  assumes sdp_a:   "sub_distrib_pconj a"
      and sdp_b:   "sub_distrib_pconj b"
      and h_wp_a:  "healthy (wp a)"
      and h_wp_b:  "healthy (wp b)"
      and h_wlp_b: "nearly_healthy (wlp b)"
      and uP:      "unitary P"
  shows "sub_distrib_pconj (aP⇙⊕ b)"
proof(rule sub_distrib_pconjI, unfold wp_eval, rule le_funI)
  fix Q::"'s  real" and R::"'s  real" and s::'s
  assume uQ: "unitary Q" and uR: "unitary R"

  have nnA: "0  P s" and nnB: "P s  1"
    using uP by auto
  note nn = nnA nnB

  have "((λs. P s * wlp a Q s + (1 - P s) * wlp b Q s) &&
         (λs. P s *  wp a R s + (1 - P s) *  wp b R s)) s =
        ((P s * wlp a Q s + (1 - P s) * wlp b Q s) +
         (P s *  wp a R s + (1 - P s) *  wp b R s))  1"
    by(simp add:exp_conj_def pconj_def)
  also have "... = P s *       (wlp a Q s + wp a R s) +
                   (1 - P s) * (wlp b Q s + wp b R s)  1"
    by(simp add:field_simps)
  also have "... = P s *       (wlp a Q s + wp a R s) +
                   (1 - P s) * (wlp b Q s + wp b R s) 
                   (P s + (1 - P s))"
    by(simp)
  also have "...  (P s *       (wlp a Q s + wp a R s)  P s) +
                   ((1 - P s) * (wlp b Q s + wp b R s)  (1 - P s))"
    by(rule tminus_add_mono)
  also have "... = (P s       * (wlp a Q s + wp a R s  1)) +
                   ((1 - P s) * (wlp b Q s + wp b R s  1))"
    by(simp add:nn tminus_left_distrib)
  also have "... = P s *       ((wlp a Q && wp a R) s) +
                   (1 - P s) * ((wlp b Q && wp b R) s)"
    by(simp add:exp_conj_def pconj_def)
  also {
    from sdp_a sdp_b uQ uR
    have "P s * (wlp a Q && wp a R) s  P s * wp a (Q && R) s"
     and "(1 - P s) * (wlp b Q && wp b R) s  (1 - P s) * wp b (Q && R) s"
       by (simp_all add: entailsD mult_left_mono nn sub_distrib_pconjD)
    hence "P s *       ((wlp a Q && wp a R) s) +
           (1 - P s) * ((wlp b Q && wp b R) s) 
           P s * wp a (Q && R) s + (1 - P s) * wp b (Q && R) s"
      by(auto)
  }
  finally show "((λs. P s * wlp a Q s + (1 - P s) * wlp b Q s) &&
                 (λs. P s *  wp a R s + (1 - P s) *  wp b R s)) s 
                P s * wp a (Q && R) s + (1 - P s) * wp b (Q && R) s" .
qed

lemma sdp_Embed:
  " P Q.  unitary P; unitary Q   t P && t Q  t (P && Q)  
   sub_distrib_pconj (Embed t)"
  by(auto simp:wp_eval)

lemma sdp_repeat:
  fixes a::"'s prog"
  assumes sdpa: "sub_distrib_pconj a"
      and hwp: "healthy (wp a)" and hwlp: "nearly_healthy (wlp a)"
  shows "sub_distrib_pconj (repeat n a)" (is "?X n")
proof(induct n)
  show "?X 0" by(simp add:sdp_Skip)
  fix n assume IH: "?X n"
  show "?X (Suc n)"
  proof(rule sub_distrib_pconjI, simp add:wp_eval)
    fix P::"'s  real" and Q::"'s  real"
    assume uP: "unitary P" and uQ: "unitary Q"
    from assms have hwlpa: "nearly_healthy (wlp (repeat n a))"
                and hwpa:  "healthy (wp (repeat n a))"
      by(auto intro:healthy_intros)
    from uP and hwlpa have "unitary (wlp (repeat n a) P)" by(blast)
    moreover from uQ and hwpa have "unitary (wp (repeat n a) Q)" by(blast)
    ultimately
    have "wlp a (wlp (repeat n a) P) && wp a (wp (repeat n a) Q) 
          wp a (wlp (repeat n a) P && wp (repeat n a) Q)"
      using sdpa by(blast)
    also {
      from hwlp have "nearly_healthy (wlp (repeat n a))" by(rule healthy_intros)
      with uP have "sound (wlp (repeat n a) P)" by(auto)
      moreover from hwp uQ have "sound (wp (repeat n a) Q)"
        by(auto intro:healthy_intros)
      ultimately have "sound (wlp (repeat n a) P && wp (repeat n a) Q)"
        by(rule exp_conj_sound)
      moreover {
        from uP uQ have "sound (P && Q)" by(auto intro:exp_conj_sound)
        with hwp have "sound (wp (repeat n a) (P && Q))"
          by(auto intro:healthy_intros)
      }
      moreover from uP uQ IH
      have "wlp (repeat n a) P && wp (repeat n a) Q  wp (repeat n a) (P && Q)"
        by(blast)
      ultimately
      have "wp a (wlp (repeat n a) P && wp (repeat n a) Q) 
            wp a (wp (repeat n a) (P && Q))"
        by(rule mono_transD[OF healthy_monoD, OF hwp])
    }
    finally show "wlp a (wlp (repeat n a) P) && wp a (wp (repeat n a) Q) 
                  wp a (wp (repeat n a) (P && Q))" .
  qed
qed

lemma sdp_SetPC:
  fixes p::"'a  's prog"
  assumes sdp: "s a. a  supp (P s)  sub_distrib_pconj (p a)"
      and fin: "s. finite (supp (P s))"
      and nnp: "s a. 0  P s a"
      and sub: "s. sum (P s) (supp (P s))  1"
  shows "sub_distrib_pconj (SetPC p P)"
proof(rule sub_distrib_pconjI, simp add:wp_eval, rule le_funI)
  fix Q::"'s  real" and R::"'s  real" and s::'s
  assume uQ: "unitary Q" and uR: "unitary R"
  have "((λs. asupp (P s). P s a * wlp (p a) Q s) &&
         (λs. asupp (P s). P s a *  wp (p a) R s)) s =
        (asupp (P s). P s a * wlp (p a) Q s) + (asupp (P s). P s a * wp (p a) R s)  1"
    by(simp add:exp_conj_def pconj_def)
  also have "... = (asupp (P s). P s a * (wlp (p a) Q s + wp (p a) R s))  1"
    by(simp add: sum.distrib field_simps)
  also from sub
  have "...  (asupp (P s). P s a * (wlp (p a) Q s + wp (p a) R s)) 
              (asupp (P s). P s a)"
    by(rule tminus_right_antimono)
  also from fin
  have "...  (asupp (P s). P s a * (wlp (p a) Q s + wp (p a) R s)  P s a)"
    by(rule tminus_sum_mono)
  also from nnp
  have "... = (asupp (P s). P s a * (wlp (p a) Q s + wp (p a) R s  1))"
    by(simp add:tminus_left_distrib)
  also have "... = (asupp (P s). P s a * (wlp (p a) Q && wp (p a) R) s)"
    by(simp add:pconj_def exp_conj_def)
  also {
    from sdp uQ uR
    have "a. a  supp (P s)  wlp (p a) Q && wp (p a) R  wp (p a) (Q && R)"
      by(blast intro:sub_distrib_pconjD)
    with nnp
    have "(asupp (P s). P s a * (wlp (p a) Q && wp (p a) R) s) 
          (asupp (P s). P s a * (wp (p a) (Q && R)) s)"
      by(blast intro:sum_mono mult_left_mono)
  }
  finally show "((λs. asupp (P s). P s a * wlp (p a) Q s) &&
                 (λs. asupp (P s). P s a * wp (p a) R s)) s 
                (asupp (P s). P s a * wp (p a) (Q && R) s)" .
qed

lemma sdp_SetDC:
  fixes p::"'a  's prog"
  assumes sdp: "s a. a  S s  sub_distrib_pconj (p a)"
      and hwp: "s a. a  S s  healthy (wp (p a))"
      and hwlp: "s a. a  S s  nearly_healthy (wlp (p a))"
      and ne:  "s. S s  {}"
  shows "sub_distrib_pconj (SetDC p S)"
proof(rule sub_distrib_pconjI, rule le_funI)
  fix P::"'s  real" and Q::"'s  real" and s::'s
  assume uP: "unitary P" and uQ: "unitary Q"

  from uP hwlp
  have "x. x  (λa. wlp (p a) P) ` S s  unitary x" by(auto)
  hence "y. y  (λa. wlp (p a) P s) ` S s  0  y" by(auto)
  hence "a. a  S s  wlp (SetDC p S) P s  wlp (p a) P s"
    unfolding wp_eval by(intro cInf_lower bdd_belowI, auto)
  moreover {
    from uQ hwp have "a. a  S s   0  wp (p a) Q s" by(blast)
    hence "a. a  S s  wp (SetDC p S) Q s  wp (p a) Q s"
    unfolding wp_eval by(intro cInf_lower bdd_belowI, auto)
  }
  ultimately
  have "a. a  S s  wlp (SetDC p S) P s + wp (SetDC p S) Q s  1 
                      wlp (p a) P s + wp (p a) Q s  1"
    by(auto intro:tminus_left_mono add_mono)
  also have "a. wlp (p a) P s + wp (p a) Q s  1 = (wlp (p a) P && wp (p a) Q) s"
    by(simp add:exp_conj_def pconj_def)
  also from sdp uP uQ
  have "a. a  S s  ... a  wp (p a) (P && Q) s"
    by(blast)
  also have "a. ... a = wp (p a) (λs. P s + Q s  1) s"
    by(simp add:exp_conj_def pconj_def)
  finally
  show "(wlp (SetDC p S) P && wp (SetDC p S) Q) s  wp (SetDC p S) (P && Q) s"
    unfolding exp_conj_def pconj_def wp_eval
    using ne by(blast intro!:cInf_greatest)
qed

lemma sdp_Bind:
  " s. sub_distrib_pconj (p (f s))   sub_distrib_pconj (Bind f p)"
  unfolding sub_distrib_pconj_def wp_eval exp_conj_def pconj_def
  by(blast)

text ‹For loops, we again appeal to our transfinite induction principle, this time taking
advantage of the simultaneous treatment of both strict and liberal transformers.›
lemma sdp_loop:
  fixes body::"'s prog"
  assumes sdp_body: "sub_distrib_pconj body"
      and hwlp: "nearly_healthy (wlp body)"
      and hwp:  "healthy (wp body)"
  shows "sub_distrib_pconj (do G  body od)"
proof(rule sub_distrib_pconjI, rule loop_induct[OF hwp hwlp])
  fix P Q::"'s expect" and S::"('s trans × 's trans) set"
  assume uP: "unitary P" and uQ: "unitary Q"
     and ffst: "xS. feasible (fst x)"
     and usnd: "xS. Q. unitary Q  unitary (snd x Q)"
     and IH: "xS. snd x P && fst x Q  fst x (P && Q)"

  show "Inf_utrans (snd ` S) P && Sup_trans (fst ` S) Q 
                  Sup_trans (fst ` S) (P && Q)"
  proof(cases)
    assume "S = {}"
    thus ?thesis
      by(simp add:Inf_trans_def Sup_trans_def Inf_utrans_def
                  Inf_exp_def Sup_exp_def exp_conj_def)
  next
    assume ne: "S  {}"

    let "?f s" = "1 + Sup_trans (fst ` S) (P && Q) s - Inf_utrans (snd ` S) P s"

    from ne obtain t where tin: "t  fst ` S" by(auto)
    from ne obtain u where uin: "u  snd ` S" by(auto)

    from tin ffst uP uQ have utPQ: "unitary (t (P && Q))"
      by(auto intro:exp_conj_unitary)
    hence "s. 0  t (P && Q) s" by(auto)
    also {
      from ffst tin have le: "le_utrans t (Sup_trans (fst ` S))"
        by(auto intro:Sup_trans_upper)
      with uP uQ have "s. t (P && Q) s  Sup_trans (fst ` S) (P && Q) s"
        by(auto intro:exp_conj_unitary)
    }
    finally have nn_rhs: "s. 0  Sup_trans (fst ` S) (P && Q) s" .

    have "R. Inf_utrans (snd ` S) P && R  Sup_trans (fst ` S) (P && Q)  R  ?f"
    proof(rule contrapos_pp, assumption)
      fix R
      assume "¬ R  ?f"
      then obtain s where "¬ R s  ?f s" by(auto)
      hence gt: "?f s < R s" by(simp)

      from nn_rhs have g1: "1  1 + Sup_trans (fst ` S) (P && Q) s" by(auto)
      hence "Sup_trans (fst ` S) (P && Q) s = Inf_utrans (snd ` S) P s .& ?f s"
        by(simp add:pconj_def)
      also from g1 have "... = Inf_utrans (snd ` S) P s + ?f s - 1"
        by(simp)
      also from gt have "... < Inf_utrans (snd ` S) P s + R s - 1"
        by(simp)
      also {
        with g1 have "1  Inf_utrans (snd ` S) P s + R s"
          by(simp)
        hence "Inf_utrans (snd ` S) P s + R s - 1 = Inf_utrans (snd ` S) P s .& R s"
          by(simp add:pconj_def)
      }
      finally
      have "¬ (Inf_utrans (snd ` S) P && R) s  Sup_trans (fst ` S) (P && Q) s"
        by(simp add:exp_conj_def)
      thus "¬ Inf_utrans (snd ` S) P && R  Sup_trans (fst ` S) (P && Q)"
        by(auto)
    qed

    moreover have "tfst ` S. Inf_utrans (snd ` S) P && t Q   Sup_trans (fst ` S) (P && Q)"
    proof
      fix t assume tin: "t  fst ` S"
      then obtain x where xin: "x  S" and fx: "t = fst x" by(auto)

      from xin have "snd x  snd ` S" by(auto)
      with uP usnd have "Inf_utrans (snd ` S) P  snd x P"
        by(auto intro:le_utransD[OF Inf_utrans_lower])
      hence "Inf_utrans (snd ` S) P && fst x Q  snd x P && fst x Q"
        by(auto intro:entails_frame)
      also from xin IH have "...  fst x (P && Q)"
        by(auto)
      also from xin ffst exp_conj_unitary[OF uP uQ]
      have "...  Sup_trans (fst ` S) (P && Q)"
        by(auto intro:le_utransD[OF Sup_trans_upper])
      finally show "Inf_utrans (snd ` S) P && t Q  Sup_trans (fst ` S) (P && Q)"
        by(simp add:fx)
    qed
    ultimately have bt: "tfst ` S. t Q  ?f" by(blast)

    have "Sup_trans (fst ` S) Q = Sup_exp {t Q |t. t  fst ` S}"
      by(simp add:Sup_trans_def)
    also have "...  ?f"
    proof(rule Sup_exp_least)
      from bt show " R{t Q |t. t  fst ` S}. R  ?f" by(blast)
      from ne obtain t where tin: "t  fst ` S" by(auto)
      with ffst uQ have "unitary (t Q)" by(auto)
      hence "λs. 0  t Q" by(auto)
      also from tin bt have "...  ?f" by(auto)
      finally show "nneg (λs. 1 + Sup_trans (fst ` S) (P && Q) s -
                          Inf_utrans (snd ` S) P s)"
        by(auto)
    qed
    finally have "Inf_utrans (snd ` S) P && Sup_trans (fst ` S) Q 
                  Inf_utrans (snd ` S) P && ?f"
      by(auto intro:entails_frame)
    also from nn_rhs have "...  Sup_trans (fst ` S) (P && Q)"
      by(simp add:exp_conj_def pconj_def)
    finally show ?thesis .
  qed

next
  fix P Q::"'s expect" and t u::"'s trans"
  assume uP: "unitary P" and uQ: "unitary Q"
     and ft: "feasible t"
     and uu: "Q. unitary Q  unitary (u Q)"
     and IH: "u P && t Q  t (P && Q)"
  show "wlp (body ;; Embed u« G »⇙⊕ Skip) P &&
        wp  (body ;; Embed t« G »⇙⊕ Skip) Q 
        wp  (body ;; Embed t« G »⇙⊕ Skip) (P && Q)"
  proof(rule le_funI, simp add:wp_eval exp_conj_def pconj_def)
    fix s::'s
    have "« G » s * wlp body (u P) s + (1 - « G » s) * P s +
          (« G » s * wp body (t Q) s + (1 - « G » s) * Q s)  1 =
          (« G » s * wlp body (u P) s + « G » s * wp body (t Q) s) +
           ((1 - « G » s) * P s + (1 - « G » s) * Q s)  («G» s + (1 - «G» s))"
      by(simp add:ac_simps)
    also have "... 
          (« G » s * wlp body (u P) s + « G » s * wp body (t Q) s  «G» s) +
           ((1 - « G » s) * P s + (1 - « G » s) * Q s  (1 - «G» s))"
      by(rule tminus_add_mono)
    also have "... =
          « G » s * (wlp body (u P) s + wp body (t Q) s  1) +
           (1 - « G » s) * (P s + Q s  1)"
       by(simp add:tminus_left_distrib distrib_left)
    also {
      from uP uQ ft uu
      have "wlp body (u P) && wp body (t Q)  wp body (u P && t Q)"
        by(auto intro:sub_distrib_pconjD[OF sdp_body])
      also from IH unitary_sound[OF uP] unitary_sound[OF uQ] ft
                   unitary_sound[OF uu[OF uP]]
      have "  wp body (t (P && Q))"
        by(blast intro!:mono_transD[OF healthy_monoD, OF hwp] exp_conj_sound)
      finally have "wlp body (u P) s + wp body (t Q) s  1 
                    wp body (t (λs. P s + Q s  1)) s"
        by(auto simp:exp_conj_def pconj_def)
      hence "« G » s * (wlp body (u P) s + wp body (t Q) s  1) +
             (1 - « G » s) * (P s + Q s  1) 
             « G » s * wp body (t (λs. P s + Q s  1)) s +
             (1 - « G » s) * (P s + Q s  1)"
        by(auto intro:add_right_mono mult_left_mono)
    }
    finally
    show "« G » s * wlp body (u P) s + (1 - « G » s) * P s +
          (« G » s * wp body (t Q) s + (1 - « G » s) * Q s)  1 
          « G » s * wp body (t (λs. P s + Q s  1)) s +
          (1 - « G » s) * (P s + Q s  1)" .
  qed
next
  fix P Q::"'s expect" and t t' u u'::"'s trans"
  assume "unitary P" "unitary Q"
         "equiv_trans t t'" "equiv_utrans u u'"
         "u P && t Q  t (P && Q)"
  thus "u' P && t' Q  t' (P && Q)"
    by(simp add:equiv_transD unitary_sound equiv_utransD exp_conj_unitary)
qed

lemmas sdp_intros =
  sdp_Abort  sdp_Skip  sdp_Apply
  sdp_Seq    sdp_DC    sdp_PC
  sdp_SetPC  sdp_SetDC sdp_Embed
  sdp_repeat sdp_Bind  sdp_loop

subsection ‹The Well-Defined Predicate.›

definition
  well_def :: "'s prog  bool"
where
  "well_def prog  healthy (wp prog)  nearly_healthy (wlp prog)
                  wp_under_wlp prog  sub_distrib_pconj prog
                  sublinear (wp prog)  bd_cts (wp prog)"

lemma well_defI[intro]:
  " healthy (wp prog); nearly_healthy (wlp prog);
     wp_under_wlp prog; sub_distrib_pconj prog; sublinear (wp prog);
     bd_cts (wp prog)  
   well_def prog"
  unfolding well_def_def by(simp)

lemma well_def_wp_healthy[dest]:
  "well_def prog  healthy (wp prog)"
  unfolding well_def_def by(simp)

lemma well_def_wlp_nearly_healthy[dest]:
  "well_def prog  nearly_healthy (wlp prog)"
  unfolding well_def_def by(simp)

lemma well_def_wp_under[dest]:
  "well_def prog  wp_under_wlp prog"
  unfolding well_def_def by(simp)

lemma well_def_sdp[dest]:
  "well_def prog  sub_distrib_pconj prog"
  unfolding well_def_def by(simp)

lemma well_def_wp_sublinear[dest]:
  "well_def prog  sublinear (wp prog)"
  unfolding well_def_def by(simp)

lemma well_def_wp_cts[dest]:
  "well_def prog  bd_cts (wp prog)"
  unfolding well_def_def by(simp)

lemmas wd_dests =
  well_def_wp_healthy well_def_wlp_nearly_healthy
  well_def_wp_under well_def_sdp
  well_def_wp_sublinear well_def_wp_cts

lemma wd_Abort:
  "well_def Abort"
  by(blast intro:healthy_wp_Abort nearly_healthy_wlp_Abort
                 wp_under_wlp_Abort sdp_Abort sublinear_wp_Abort
                 cts_wp_Abort)

lemma wd_Skip:
  "well_def Skip"
  by(blast intro:healthy_wp_Skip nearly_healthy_wlp_Skip
                 wp_under_wlp_Skip sdp_Skip sublinear_wp_Skip
                 cts_wp_Skip)

lemma wd_Apply:
  "well_def (Apply f)"
  by(blast intro:healthy_wp_Apply nearly_healthy_wlp_Apply
                 wp_under_wlp_Apply sdp_Apply sublinear_wp_Apply
                 cts_wp_Apply)

lemma wd_Seq:
  " well_def a; well_def b   well_def (a ;; b)"
  by(blast intro:healthy_wp_Seq nearly_healthy_wlp_Seq
                 wp_under_wlp_Seq sdp_Seq sublinear_wp_Seq
                 cts_wp_Seq)

lemma wd_PC:
  " well_def a; well_def b; unitary P   well_def (aP⇙⊕ b)"
  by(blast intro:healthy_wp_PC nearly_healthy_wlp_PC
                 wp_under_wlp_PC sdp_PC sublinear_wp_PC
                 cts_wp_PC)

lemma wd_DC:
  " well_def a; well_def b   well_def (a  b)"
  by(blast intro:healthy_wp_DC nearly_healthy_wlp_DC
                 wp_under_wlp_DC sdp_DC sublinear_wp_DC
                 cts_wp_DC)

lemma wd_SetDC:
  " x s. x  S s  well_def (a x); s. S s  {};
     s. finite (S s)   well_def (SetDC a S)"
by (simp add: cts_wp_SetDC ex_in_conv healthy_intros(17) healthy_intros(18) sdp_intros(8) sublinear_intros(8) well_def_def wp_under_wlp_intros(8))


lemma wd_SetPC:
  " x s. x  (supp (p s))  well_def (a x); s. unitary (p s); s. finite (supp (p s));
     s. sum (p s) (supp (p s))  1   well_def (SetPC a p)"
  by(iprover intro!:well_defI healthy_wp_SetPC nearly_healthy_wlp_SetPC
                    wp_under_wlp_SetPC sdp_SetPC sublinear_wp_SetPC cts_wp_SetPC
             dest:wd_dests unitary_sound sound_nneg[OF unitary_sound] nnegD)

lemma wd_Embed:
  fixes t::"'s trans"
  assumes ht: "healthy t" and st: "sublinear t" and ct: "bd_cts t"
  shows "well_def (Embed t)"
proof(intro well_defI)
  from ht show "healthy (wp (Embed t))" "nearly_healthy (wlp (Embed t))"
    by(simp add:wp_def wlp_def Embed_def healthy_nearly_healthy)+
  from st show "sublinear (wp (Embed t))" by(simp add:wp_def Embed_def)
  show "wp_under_wlp (Embed t)" by(simp add:wp_under_wlp_def wp_eval)
  show "sub_distrib_pconj (Embed t)"
    by(rule sub_distrib_pconjI,
       auto intro:le_funI[OF sublinearD[OF st, where a=1 and b=1 and c=1, simplified]]
            simp:exp_conj_def pconj_def wp_def wlp_def Embed_def)
  from ct show "bd_cts (wp (Embed t))"
    by(simp add:wp_def Embed_def)
qed

lemma wd_repeat:
  "well_def a  well_def (repeat n a)"
  by(blast intro:healthy_wp_repeat nearly_healthy_wlp_repeat
                 wp_under_wlp_repeat sdp_repeat sublinear_wp_repeat cts_wp_repeat)

lemma wd_Bind:
  " s. well_def (a (f s))   well_def (Bind f a)"
  by(blast intro:healthy_wp_Bind nearly_healthy_wlp_Bind
                 wp_under_wlp_Bind sdp_Bind sublinear_wp_Bind cts_wp_Bind)

lemma wd_loop:
  "well_def body  well_def (do G  body od)"
  by(blast intro:healthy_wp_loop nearly_healthy_wlp_loop
                 wp_under_wlp_loop sdp_loop sublinear_wp_loop cts_wp_loop)

lemmas wd_intros =
  wd_Abort wd_Skip   wd_Apply
  wd_Embed wd_Seq    wd_PC
  wd_DC    wd_SetPC  wd_SetDC
  wd_Bind  wd_repeat wd_loop

end