           (*-------------------------------------------*
            |        CSP-Prover on Isabelle2004         |
            |               December 2004               |
            |                   July 2005  (modified)   |
            |                 August 2005  (modified)   |
            |                                           |
            |        CSP-Prover on Isabelle2005         |
            |                October 2005  (modified)   |
            |               November 2005  (modified)   |
            |                  March 2007  (modified)   |
            |                 August 2007  (modified)   |
            |                                           |
            |        CSP-Prover on Isabelle2008         |
            |                   June 2008  (modified)   |
            |                                           |
            |        CSP-Prover on Isabelle2009-2       |
            |                October 2010  (modified)   |
            |                                           |
            |        CSP-Prover on Isabelle2016         |
            |                    May 2016  (modified)   |
            |                                           |
            |        Yoshinao Isobe (AIST JAPAN)        |
            *-------------------------------------------*)

theory CSP_T_contraction
imports CSP_T_traces RS_prod
begin

(*****************************************************************

         1. contraction traces
         2. contraction [[ ]]Tfun

 *****************************************************************)

(*============================================*
 |                   gSKIP                    |
 *============================================*)

lemma gSKIP_to_Tick_notin_traces:
  "ALL P M. gSKIP P --> <Tick> ~:t traces (P) M"
apply (intro allI)
apply (induct_tac P)
apply (simp_all add: in_traces)
apply (intro impI conjI allI)

 apply (erule noTick_rmTickE)
 apply (simp)
 apply (auto)
 apply (erule noTick_rmTickE)
 apply (simp)
 apply (erule noTick_rmTickE)
 apply (simp)
done

(*--------------------------------*
 |        STOP,SKIP,DIV           |
 *--------------------------------*)

(*** Constant_contraction ***)

lemma map_alpha_Constant: "0 <= alpha ==> map_alpha (%M. C) alpha"
apply (simp add: map_alpha_def)
(* for Isabelle 2013
apply (simp add: real_mult_order_eq)
*)
done

(*** non_expanding_Constant ***)

lemma non_expanding_Constant: "non_expanding (%M. C)"
by (simp add: non_expanding_def map_alpha_Constant)

(*** Constant_contraction_alpha ***)

lemma contraction_alpha_Constant:
   "[| 0 <= alpha ; 1 > alpha |] ==> contraction_alpha (%M. C) alpha"
by (simp add: contraction_alpha_def map_alpha_Constant)

(*** STOP ***)

lemma map_alpha_traces_STOP:
  "0 <= alpha ==> map_alpha (traces (STOP)) alpha"
by (simp add: traces_iff map_alpha_Constant)

lemma non_expanding_traces_STOP:
  "non_expanding (traces (STOP))"
by (simp add: non_expanding_def map_alpha_traces_STOP)

lemma contraction_alpha_traces_STOP:
  "[| 0 <= alpha ; 1 > alpha |] ==> contraction_alpha (traces (STOP)) alpha"
by (simp add: traces_iff contraction_alpha_Constant)

(*** SKIP ***)

lemma map_alpha_traces_SKIP:
  "0 <= alpha ==> map_alpha (traces (SKIP)) alpha"
by (simp add: traces_iff map_alpha_Constant)

lemma non_expanding_traces_SKIP:
  "non_expanding (traces (SKIP))"
by (simp add: non_expanding_def map_alpha_traces_SKIP)

lemma contraction_alpha_traces_SKIP:
  "[| 0 <= alpha ; 1 > alpha |] ==> contraction_alpha (traces (SKIP)) alpha"
by (simp add: traces_iff contraction_alpha_Constant)

(*** DIV ***)

lemma map_alpha_traces_DIV:
  "0 <= alpha ==> map_alpha (traces (DIV)) alpha"
by (simp add: traces_iff map_alpha_Constant)

lemma non_expanding_traces_DIV:
  "non_expanding (traces (DIV))"
by (simp add: non_expanding_def map_alpha_traces_DIV)

lemma contraction_alpha_traces_DIV:
  "[| 0 <= alpha ; 1 > alpha |] ==> contraction_alpha (traces (DIV)) alpha"
by (simp add: traces_iff contraction_alpha_Constant)

(*--------------------------------*
 |          Act_prefix            |
 *--------------------------------*)

lemma contraction_half_traces_Act_prefix_lm:
   "distance (traces (a -> P) M1, traces (a -> Q) M2) * 2
    = distance (traces P M1, traces Q M2)"
apply (rule sym)
apply (simp add: to_distance_rs)

apply (rule rest_Suc_dist_half[simplified])
apply (rule allI)
apply (simp add: rest_domT_eq_iff)
apply (rule iffI)

 (* => *)
 apply (rule allI)
 apply (simp add: in_traces)
 apply (rule iffI)

  (* => *)
  apply (elim conjE exE disjE, simp)
  apply (drule_tac x="sa" in spec)
  apply (simp)
  (* <= *)
  apply (elim conjE exE disjE, simp)
  apply (drule_tac x="sa" in spec)
  apply (simp)

 (* <= *)
 apply (rule allI)
 apply (drule_tac x="<Ev a> ^^^ s" in spec)
 apply (simp add: in_traces)
done

(***  contraction_half ***)

lemma contraction_half_traces_Act_prefix:
 "non_expanding (traces P)
  ==> contraction_alpha (traces (a -> P)) (1 / 2)"
apply (simp add: contraction_alpha_def non_expanding_def map_alpha_def)
apply (intro allI)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (simp add: contraction_half_traces_Act_prefix_lm)
done

(***  contraction ***)

lemma contraction_traces_Act_prefix:
 "non_expanding (traces P)
  ==> contraction (traces (a -> P))"
apply (simp add: contraction_def)
apply (rule_tac x="1/2" in exI)
by (simp add: contraction_half_traces_Act_prefix)

(*** non_expanding ***)

lemma non_expanding_traces_Act_prefix:
 "non_expanding (traces P)
  ==> non_expanding (traces (a -> P))"
apply (rule contraction_non_expanding)
by (simp add: contraction_traces_Act_prefix)

(*--------------------------------*
 |        Ext_pre_choice          |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma Ext_pre_choice_Act_prefix_rest_domT_sub:
   "[| ALL a : X.
         traces (a -> Pf a) M1 .|. n <= traces (a -> Qf a) M2 .|. n |]
    ==> traces (? a:X -> Pf a) M1 .|. n <=
        traces (? a:X -> Qf a) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
apply (elim conjE exE disjE)
apply (simp_all)

apply (drule_tac x="a" in bspec, simp)
apply (drule_tac x="t" in spec)
apply (simp add: in_traces)
done

(*** rest_domT (equal) ***)

lemma Ext_pre_choice_Act_prefix_rest_domT:
   "[| ALL a : X.
         traces (a -> Pf a) M1 .|. n = traces (a -> Qf a) M2 .|. n |]
    ==> traces (? a:X -> Pf a) M1 .|. n =
        traces (? a:X -> Qf a) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: Ext_pre_choice_Act_prefix_rest_domT_sub)

(*** distT lemma ***)

lemma Ext_pre_choice_Act_prefix_distT_nonempty:
"[| X ~= {} ; PQs = {(traces (a -> Pf a) M1, traces (a -> Qf a) M2)|a. a : X} |]
 ==> (EX PQ. PQ:PQs &
             distance(traces (? a:X -> Pf a) M1, traces (? a:X -> Qf a) M2)
          <= distance(fst PQ, snd PQ))"
apply (simp only: to_distance_rs)
apply (rule rest_to_dist_pair)
apply (force)

apply (intro allI impI)
apply (rule Ext_pre_choice_Act_prefix_rest_domT)
apply (rule ballI)
apply (simp)
apply (drule_tac x="traces (a -> Pf a) M1" in spec)
apply (drule_tac x="traces (a -> Qf a) M2" in spec)
by (auto)

(*** contraction lemma ***)

lemma contraction_half_traces_Ext_pre_choice_lm:
  "[| X ~= {} ; ALL a. distance (traces (Pf a) M1, traces (Qf a) M2)
                    <= distance (x1, x2) |]
    ==> distance (traces (? a:X -> Pf a) M1, traces (? a:X -> Qf a) M2) * 2
     <= distance (x1, x2)"
apply (insert Ext_pre_choice_Act_prefix_distT_nonempty
       [of X "{(traces (a -> Pf a) M1, traces (a -> Qf a) M2) |a. a : X}" Pf M1 Qf M2])
apply (simp)
apply (elim conjE exE)
apply (simp)
apply (subgoal_tac
    "distance (traces (aa -> Pf aa) M1, traces (aa -> Qf aa) M2) * 2
   = distance (traces (Pf aa) M1, traces (Qf aa) M2)")
apply (drule_tac x="aa" in spec)
apply (force)
by (simp add: contraction_half_traces_Act_prefix_lm)

(*** contraction_half ***)

lemma contraction_half_traces_Ext_pre_choice:
 "ALL a. non_expanding (traces (Pf a))
  ==> contraction_alpha (traces (? :X -> Pf)) (1 / 2)"
apply (simp add: contraction_alpha_def non_expanding_def map_alpha_def)
apply (case_tac "X = {}")
apply (simp add: traces_iff)
by (simp add: contraction_half_traces_Ext_pre_choice_lm)

(*** Ext_pre_choice_evalT_contraction ***)

lemma contraction_traces_Ext_pre_choice:
 "ALL a. non_expanding (traces (Pf a))
  ==> contraction (traces (? :X -> Pf))"
apply (simp add: contraction_def)
apply (rule_tac x="1/2" in exI)
by (simp add: contraction_half_traces_Ext_pre_choice)

(*** Ext_pre_choice_evalT_non_expanding ***)

lemma non_expanding_traces_Ext_pre_choice:
 "ALL a. non_expanding (traces (Pf a))
  ==> non_expanding (traces (? :X -> Pf))"
apply (rule contraction_non_expanding)
by (simp add: contraction_traces_Ext_pre_choice)

(*--------------------------------*
 |          Ext_choice            |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma Ext_choice_rest_domT_sub:
   "[| traces P1 M1 .|. n <= traces P2 M2 .|. n ;
       traces Q1 M1 .|. n <= traces Q2 M2 .|. n  |]
    ==> traces (P1 [+] Q1) M1 .|. n <= traces (P2 [+] Q2) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
by (auto)

(*** rest_domT (equal) ***)

lemma Ext_choice_rest_domT:
   "[| traces P1 M1 .|. n = traces P2 M2 .|. n ;
       traces Q1 M1 .|. n = traces Q2 M2 .|. n  |]
    ==> traces (P1 [+] Q1) M1 .|. n = traces (P2 [+] Q2) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: Ext_choice_rest_domT_sub)

(*** distT lemma ***)

lemma Ext_choice_distT:
"PQs = {(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}
 ==> (EX PQ. PQ:PQs &
             distance(traces (P1 [+] Q1) M1, traces (P2 [+] Q2) M2)
          <= distance((fst PQ), (snd PQ)))"
apply (simp only: to_distance_rs)
apply (rule rest_to_dist_pair)
by (auto intro: Ext_choice_rest_domT)

(*** map_alpha T lemma ***)

lemma map_alpha_traces_Ext_choice_lm:
  "[| distance (traces P1 M1, traces P2 M2) <= alpha * distance (x1, x2) ;
      distance (traces Q1 M1, traces Q2 M2) <= alpha * distance (x1, x2) |]
    ==> distance (traces (P1 [+] Q1) M1, traces (P2 [+] Q2) M2)
     <= alpha * distance (x1, x2)"
apply (insert Ext_choice_distT)
apply (insert Ext_choice_distT
       [of "{(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}"
           P1 M1 P2 M2 Q1 Q2])
by (auto)

(*** map_alpha ***)

lemma map_alpha_traces_Ext_choice:
 "[| map_alpha (traces P) alpha ; map_alpha (traces Q) alpha |]
  ==> map_alpha (traces (P [+] Q)) alpha"
apply (simp add: map_alpha_def)
apply (intro allI)
apply (erule conjE)
apply (drule_tac x="x" in spec)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (drule_tac x="y" in spec)
by (simp add: map_alpha_traces_Ext_choice_lm)

(*** non_expanding ***)

lemma non_expanding_traces_Ext_choice:
 "[| non_expanding (traces P) ; non_expanding (traces Q) |]
  ==> non_expanding (traces (P [+] Q))"
by (simp add: non_expanding_def map_alpha_traces_Ext_choice)

(*** contraction ***)

lemma contraction_alpha_traces_Ext_choice:
 "[| contraction_alpha (traces P) alpha ;
     contraction_alpha (traces Q) alpha|]
  ==> contraction_alpha (traces (P [+] Q)) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_Ext_choice)

(*--------------------------------*
 |          Int_choice            |
 *--------------------------------*)

(*** map_alpha ***)

lemma map_alpha_traces_Int_choice:
 "[| map_alpha (traces P) alpha ; map_alpha (traces Q) alpha |]
  ==> map_alpha (traces (P |~| Q)) alpha"
by (simp add: map_alpha_traces_Ext_choice traces_Int_choice_Ext_choice)

(*** non_expanding ***)

lemma non_expanding_traces_Int_choice:
 "[| non_expanding (traces P) ; non_expanding (traces Q) |]
  ==> non_expanding (traces (P |~| Q))"
by (simp add: non_expanding_traces_Ext_choice traces_Int_choice_Ext_choice)

(*** contraction ***)

lemma contraction_alpha_traces_Int_choice:
 "[| contraction_alpha (traces P) alpha ;
     contraction_alpha (traces Q) alpha|]
  ==> contraction_alpha (traces (P |~| Q)) alpha"
by (simp add: contraction_alpha_traces_Ext_choice traces_Int_choice_Ext_choice)

(*--------------------------------*
 |        Rep_int_choice          |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma Rep_int_choice_rest_domT_sub:
   "[| ALL c : sumset C.
         traces (Pf c) M1 .|. n <= traces (Qf c) M2 .|. n |]
    ==>  traces (!! :C .. Pf) M1 .|. n <=
         traces (!! :C .. Qf) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
by (auto)

(*** rest_domT (equal) ***)

lemma Rep_int_choice_rest_domT:
   "[| ALL c : sumset C.
         traces (Pf c) M1 .|. n = traces (Qf c) M2 .|. n |]
    ==>  traces (!! :C .. Pf) M1 .|. n =
         traces (!! :C .. Qf) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: Rep_int_choice_rest_domT_sub)

(*** distT lemma ***)

lemma Rep_int_choice_distT_nonempty:
"[| sumset C ~= {} ;
    PQs = {(traces (Pf c) M1, traces (Qf c) M2)|c. c : sumset C} |]
 ==> (EX PQ. PQ:PQs &
             distance(traces (!! :C .. Pf) M1, traces (!! :C .. Qf) M2)
          <= distance(fst PQ, snd PQ))"
apply (simp only: to_distance_rs)
apply (rule rest_to_dist_pair)
apply (fast)

apply (intro allI impI)
apply (rule Rep_int_choice_rest_domT)
by (auto)

(*** map_alpha T lemma ***)

lemma map_alpha_traces_Rep_int_choice_lm:
"[| sumset C ~= {} ;
    ALL c. distance (traces (Pf c) M1, traces (Qf c) M2)
                  <= alpha * distance (x1, x2) |]
 ==> distance(traces(!! :C .. Pf) M1, (traces(!! :C .. Qf) M2))
  <= alpha * distance(x1, x2)"
apply (insert Rep_int_choice_distT_nonempty
       [of C "{(traces (Pf c) M1, traces (Qf c) M2)|c. c : sumset C}"
           Pf M1 Qf M2])
apply (simp)
apply (elim conjE exE, simp)
apply (drule_tac x="c" in spec)
by (force)

(*** map_alpha ***)
lemma map_alpha_traces_Rep_int_choice:
 "ALL c. map_alpha (traces (Pf c)) alpha
  ==> map_alpha (traces (!! :C .. Pf)) alpha"
apply (simp add: map_alpha_def)
apply (case_tac "sumset C = {}")
 apply (simp add: traces_iff)
(* for Isabelle 2013
apply (simp add: real_mult_order_eq)
*)
apply (simp add: map_alpha_traces_Rep_int_choice_lm)
done

(*** non_expanding ***)

lemma non_expanding_traces_Rep_int_choice:
 "ALL c. non_expanding (traces (Pf c))
  ==> non_expanding (traces (!! :C .. Pf))"
by (simp add: non_expanding_def map_alpha_traces_Rep_int_choice)

(*** Rep_int_choice_evalT_contraction_alpha ***)

lemma contraction_alpha_traces_Rep_int_choice:
 "ALL c. contraction_alpha (traces (Pf c)) alpha
     ==> contraction_alpha (traces (!! :C .. Pf)) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_Rep_int_choice)

(*--------------------------------*
 |               IF               |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma IF_rest_domT_sub:
   "[| traces P1 M1 .|. n <= traces P2 M2 .|. n ;
       traces Q1 M1 .|. n <= traces Q2 M2 .|. n  |]
    ==> traces (IF b THEN P1 ELSE Q1) M1 .|. n <=
        traces (IF b THEN P2 ELSE Q2) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
done

(*** rest_domT (equal) ***)

lemma IF_rest_domT:
   "[| traces P1 M1 .|. n = traces P2 M2 .|. n ;
       traces Q1 M1 .|. n = traces Q2 M2 .|. n  |]
    ==> traces (IF b THEN P1 ELSE Q1) M1 .|. n =
        traces (IF b THEN P2 ELSE Q2) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: IF_rest_domT_sub)

(*** distT lemma ***)

lemma IF_distT:
   "PQs = {(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}
    ==> (EX PQ. PQ:PQs &
             distance(traces (IF b THEN P1 ELSE Q1) M1,
                      traces (IF b THEN P2 ELSE Q2) M2)
          <= distance((fst PQ), (snd PQ)))"
apply (simp only: to_distance_rs)
apply (rule rest_to_dist_pair)
by (auto intro: IF_rest_domT)

(*** map_alpha T lemma (not used) ***)

lemma map_alpha_traces_IF_lm:
  "[| distance (traces P1 M1, traces P2 M2) <= alpha * distance (x1, x2) ;
      distance (traces Q1 M1, traces Q2 M2) <= alpha * distance (x1, x2) |]
    ==> distance(traces (IF b THEN P1 ELSE Q1) M1,
                 traces (IF b THEN P2 ELSE Q2) M2)
     <= alpha * distance (x1, x2)"
apply (insert IF_distT
       [of "{(traces P1 M1, traces P2 M2),
             (traces Q1 M1, traces Q2 M2)}"  P1 M1 P2 M2 Q1 Q2 b])
by (auto)

(*** map_alpha ***)

lemma map_alpha_traces_IF:
 "[| map_alpha (traces P) alpha ; map_alpha (traces Q) alpha |]
  ==> map_alpha (traces (IF b THEN P ELSE Q)) alpha"
apply (simp add: map_alpha_def)
apply (intro allI)
apply (erule conjE)
apply (drule_tac x="x" in spec)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (drule_tac x="y" in spec)
apply (simp add: traces_iff)
done

(*** non_expanding ***)

lemma non_expanding_traces_IF:
 "[| non_expanding (traces P) ; non_expanding (traces Q) |]
  ==> non_expanding (traces (IF b THEN P ELSE Q))"
by (simp add: non_expanding_def map_alpha_traces_IF)

(*** contraction_alpha ***)

lemma contraction_alpha_traces_IF:
 "[| contraction_alpha (traces P) alpha ; contraction_alpha (traces Q) alpha|]
  ==> contraction_alpha (traces (IF b THEN P ELSE Q)) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_IF)

(*--------------------------------*
 |           Parallel             |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma Parallel_rest_domT_sub:
   "[| traces P1 M1 .|. n <= traces P2 M2 .|. n ;
       traces Q1 M1 .|. n <= traces Q2 M2 .|. n  |]
    ==> traces (P1 |[X]| Q1) M1 .|. n <= traces (P2 |[X]| Q2) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
apply (elim conjE exE)
apply (rule_tac x="s" in exI)
apply (rule_tac x="ta" in exI)
apply (simp)

apply (erule par_tr_lengthtE)
by (auto)

(*** rest_domT (equal) ***)

lemma Parallel_rest_domT:
   "[| traces P1 M1 .|. n = traces P2 M2 .|. n ;
       traces Q1 M1 .|. n = traces Q2 M2 .|. n  |]
    ==> traces (P1 |[X]| Q1) M1 .|. n = traces (P2 |[X]| Q2) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: Parallel_rest_domT_sub)

(*** distT lemma ***)

lemma Parallel_distT:
"PQs = {(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}
 ==> (EX PQ. PQ:PQs &
             distance(traces (P1 |[X]| Q1) M1, traces (P2 |[X]| Q2) M2)
          <= distance((fst PQ), (snd PQ)))"
apply (simp only: to_distance_rs)
apply (rule rest_to_dist_pair)
by (auto intro: Parallel_rest_domT)

(*** map_alpha T lemma ***)

lemma map_alpha_traces_Parallel_lm:
  "[| distance (traces P1 M1, traces P2 M2) <= alpha * distance (x1, x2) ;
      distance (traces Q1 M1, traces Q2 M2) <= alpha * distance (x1, x2) |]
    ==> distance (traces (P1 |[X]| Q1) M1, traces (P2 |[X]| Q2) M2)
     <= alpha * distance (x1, x2)"
apply (insert Parallel_distT
       [of "{(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}"
           P1 M1 P2 M2 Q1 Q2 X])
by (auto)

(*** map_alpha ***)

lemma map_alpha_traces_Parallel:
 "[| map_alpha (traces P) alpha ; map_alpha (traces Q) alpha |]
  ==> map_alpha (traces (P |[X]| Q)) alpha"
apply (simp add: map_alpha_def)
apply (intro allI)
apply (erule conjE)
apply (drule_tac x="x" in spec)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (drule_tac x="y" in spec)
by (simp add: map_alpha_traces_Parallel_lm)

(*** non_expanding ***)

lemma non_expanding_traces_Parallel:
 "[| non_expanding (traces P) ; non_expanding (traces Q) |]
  ==> non_expanding (traces (P |[X]| Q))"
by (simp add: non_expanding_def map_alpha_traces_Parallel)

(*** contraction_alpha ***)

lemma contraction_alpha_traces_Parallel:
 "[| contraction_alpha (traces P) alpha ;
     contraction_alpha (traces Q) alpha |]
  ==> contraction_alpha (traces (P |[X]| Q)) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_Parallel)

(*--------------------------------*
 |            Hiding              |
 *--------------------------------*)

(* cms rules for Hiding is not necessary
   because processes are guarded. *)

(*--------------------------------*
 |           Renaming             |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma Renaming_rest_domT_sub:
   "traces P M1 .|. n <= traces Q M2 .|. n
    ==> traces (P [[r]]) M1 .|. n <= traces (Q [[r]]) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
apply (elim conjE exE)
apply (rule_tac x="s" in exI)
apply (drule_tac x="s" in spec)
by (simp add: ren_tr_lengtht)

(*** rest_domT (equal) ***)

lemma Renaming_rest_domT:
   "traces P M1 .|. n = traces Q M2 .|. n
    ==> traces (P [[r]]) M1 .|. n = traces (Q [[r]]) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: Renaming_rest_domT_sub)

(*** distT lemma ***)

lemma Renaming_distT:
     "distance(traces (P [[r]]) M1, traces (Q [[r]]) M2) <=
      distance(traces P M1, traces Q M2)"
apply (simp only: to_distance_rs)
apply (rule rest_distance_subset)
by (auto intro: Renaming_rest_domT)

(*** map_alphaT lemma ***)

lemma map_alpha_traces_Renaming_lm:
  "distance(traces P M1, traces Q M2) <= alpha * distance (x1, x2)
    ==> distance(traces (P [[r]]) M1, traces (Q [[r]]) M2)
     <= alpha * distance(x1, x2)"
apply (insert Renaming_distT[of P r M1 Q M2])
by (simp)

(*** map_alpha ***)

lemma map_alpha_traces_Renaming:
 "map_alpha (traces P) alpha
  ==> map_alpha (traces (P [[r]])) alpha"
apply (simp add: map_alpha_def)
apply (intro allI)
apply (erule conjE)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
by (simp add: map_alpha_traces_Renaming_lm)

(*** non_expanding ***)

lemma non_expanding_traces_Renaming:
 "non_expanding (traces P)
  ==> non_expanding (traces (P [[r]]))"
by (simp add: non_expanding_def map_alpha_traces_Renaming)

(*** contraction_alpha ***)

lemma contraction_alpha_traces_Renaming:
 "contraction_alpha (traces P) alpha
  ==> contraction_alpha (traces (P [[r]])) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_Renaming)

(*--------------------------------*
 |           Seq_compo            |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma Seq_compo_rest_domT_sub:
   "[| traces P1 M1 .|. n <= traces P2 M2 .|. n ;
       traces Q1 M1 .|. n <= traces Q2 M2 .|. n  |]
    ==> traces (P1 ;; Q1) M1 .|. n <= traces (P2 ;; Q2) M2 .|. n"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
apply (elim conjE exE disjE)

 (* case 1 *)
 apply (rule disjI1)
 apply (insert trace_last_noTick_or_Tick)
 apply (rotate_tac -1)
 apply (drule_tac x="s" in spec)
 apply (erule disjE)
 apply (rule_tac x="s" in exI, simp)

 apply (elim conjE exE)
 apply (rule_tac x="sa" in exI, simp)
 apply (drule_tac x="sa" in spec, simp)
 apply (drule mp)
 apply (rule memT_prefix_closed, simp_all, simp)

 (* case 2 *)
 apply (case_tac "~ noTick s", simp)
 apply (insert trace_last_nil_or_unnil)
 apply (rotate_tac -1)
 apply (drule_tac x="ta" in spec)
 apply (erule disjE)

  apply (rule disjI1)                (* ta = []t *)
  apply (rule_tac x="s" in exI, simp)
  apply (drule_tac x="s" in spec, simp)
  apply (drule mp)
  apply (rule memT_prefix_closed, simp_all, simp)

  apply (rule disjI2)                (* ta ~= []t *)
  apply (elim conjE exE, simp)

  apply (drule_tac x="s ^^^ <Tick>" in spec)
  apply (drule_tac x=" sa ^^^ <a>" in spec)
  apply (simp)
  apply (rule_tac x="s" in exI)
  apply (rule_tac x="sa ^^^ <a>" in exI)
  apply (simp)
done

(*** rest_domT (equal) ***)

lemma Seq_compo_rest_domT:
   "[| traces P1 M1 .|. n = traces P2 M2 .|. n ;
       traces Q1 M1 .|. n = traces Q2 M2 .|. n  |]
    ==> traces (P1 ;; Q1) M1 .|. n = traces (P2 ;; Q2) M2 .|. n"
apply (rule order_antisym)
by (simp_all add: Seq_compo_rest_domT_sub)

(*** distT lemma ***)

lemma Seq_compo_distT:
"PQs = {(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}
 ==> (EX PQ. PQ:PQs &
             distance(traces (P1 ;; Q1) M1, traces (P2 ;; Q2) M2)
          <= distance((fst PQ), (snd PQ)))"
apply (simp only: to_distance_rs)
apply (rule rest_to_dist_pair)
by (auto intro: Seq_compo_rest_domT)

(*** map_alpha T lemma ***)

lemma map_alpha_traces_Seq_compo_lm:
  "[| distance (traces P1 M1, traces P2 M2) <= alpha * distance (x1, x2) ;
      distance (traces Q1 M1, traces Q2 M2) <= alpha * distance (x1, x2) |]
    ==> distance (traces (P1 ;; Q1) M1, traces (P2 ;; Q2) M2)
     <= alpha * distance (x1, x2)"
apply (insert Seq_compo_distT)
apply (insert Seq_compo_distT
       [of "{(traces P1 M1, traces P2 M2), (traces Q1 M1, traces Q2 M2)}"
           P1 M1 P2 M2 Q1 Q2])
by (auto)

(*** map_alpha ***)

lemma map_alpha_traces_Seq_compo:
 "[| map_alpha (traces P) alpha ; map_alpha (traces Q) alpha |]
  ==> map_alpha (traces (P ;; Q)) alpha"
apply (simp add: map_alpha_def)
apply (intro allI)
apply (erule conjE)
apply (drule_tac x="x" in spec)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (drule_tac x="y" in spec)
by (simp add: map_alpha_traces_Seq_compo_lm)

(*** non_expanding ***)

lemma non_expanding_traces_Seq_compo:
 "[| non_expanding (traces P) ; non_expanding (traces Q) |]
  ==> non_expanding (traces (P ;; Q))"
by (simp add: non_expanding_def map_alpha_traces_Seq_compo)

(*** contraction_alpha ***)

lemma contraction_alpha_traces_Seq_compo:
 "[| contraction_alpha (traces P) alpha ;
     contraction_alpha (traces Q) alpha|]
  ==> contraction_alpha (traces (P ;; Q)) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_Seq_compo)

(*--------------------------------*
 |       Seq_compo  (gSKIP)       |
 *--------------------------------*)

(*** rest_domT (subset) ***)

lemma gSKIP_Seq_compo_rest_domT_sub:
   "[| traces P1 M1 .|. (Suc n) <= traces P2 M2 .|. (Suc n) ;
       traces Q1 M1 .|. n <= traces Q2 M2 .|. n ;
       <Tick> ~:t traces P1 M1 ;
       <Tick> ~:t traces P2 M2 |]
    ==> traces (P1 ;; Q1) M1 .|. (Suc n) <= traces (P2 ;; Q2) M2 .|. (Suc n)"
apply (simp add: subdomT_iff)
apply (intro allI impI)
apply (simp add: in_rest_domT)
apply (simp add: in_traces)
apply (elim conjE exE disjE)

 (* case 1 *)
 apply (rule disjI1)
 apply (insert trace_last_noTick_or_Tick)
 apply (rotate_tac -1)
 apply (drule_tac x="s" in spec)
 apply (erule disjE)
 apply (rule_tac x="s" in exI, simp)

 apply (elim conjE exE)
 apply (rule_tac x="sa" in exI, simp)
 apply (drule_tac x="sa" in spec, simp)
 apply (drule mp)
 apply (rule memT_prefix_closed, simp_all, simp)

 (* case 2 *)
 apply (case_tac "~ noTick s", simp)
 apply (insert trace_last_nil_or_unnil)
 apply (rotate_tac -1)
 apply (drule_tac x="ta" in spec)
 apply (erule disjE)

  apply (rule disjI1)                (* ta = []t *)
  apply (rule_tac x="s" in exI, simp)
  apply (drule_tac x="s" in spec, simp)
  apply (drule mp)
  apply (rule memT_prefix_closed, simp_all, simp)

  apply (rule disjI2)                (* ta ~= []t *)
  apply (elim conjE exE, simp)

  apply (drule_tac x="s ^^^ <Tick>" in spec)
  apply (drule_tac x=" sa ^^^ <a>" in spec)
  apply (simp)
  apply (rule_tac x="s" in exI)
  apply (rule_tac x="sa ^^^ <a>" in exI)
  apply (simp)

 apply (insert trace_last_nil_or_unnil)
 apply (rotate_tac -1)
 apply (drule_tac x="s" in spec)
 apply (erule disjE)
  apply (auto)
done

(*** rest_domT (equal) ***)

lemma gSKIP_Seq_compo_rest_domT:
   "[| traces P1 M1 .|. (Suc n) = traces P2 M2 .|. (Suc n) ;
       traces Q1 M1 .|. n = traces Q2 M2 .|. n ;
       <Tick> ~:t traces P1 M1 ;
       <Tick> ~:t traces P2 M2 |]
    ==> traces (P1 ;; Q1) M1 .|. (Suc n) = traces (P2 ;; Q2) M2 .|. (Suc n)"
apply (rule order_antisym)
by (simp_all add: gSKIP_Seq_compo_rest_domT_sub)

(*** map_alpha T lemma ***)

lemma gSKIP_map_alpha_traces_Seq_compo_lm:
  "[| distance (traces P1 M1, traces P2 M2) * 2 <= (1/2)^n ;
      distance (traces Q1 M1, traces Q2 M2) <= (1/2)^n ;
       <Tick> ~:t traces P1 M1 ;
       <Tick> ~:t traces P2 M2 |]
    ==> distance (traces (P1 ;; Q1) M1, traces (P2 ;; Q2) M2) * 2
     <= (1/2)^n"
apply (insert gSKIP_Seq_compo_rest_domT)
apply (insert gSKIP_Seq_compo_rest_domT[of P1 M1 n P2 M2 Q1 Q2])
apply (simp only: to_distance_rs)
apply (simp add: distance_rs_le_1)
done

(*** map_alpha ***)

lemma gSKIP_contraction_half_traces_Seq_compo:
 "[| contraction_alpha (traces P) (1/2) ; non_expanding (traces Q) ;
     gSKIP P |]
  ==> contraction_alpha (traces (P ;; Q)) (1/2)"
apply (simp add: contraction_alpha_def non_expanding_def map_alpha_def)
apply (intro allI)
apply (drule_tac x="x" in spec)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (drule_tac x="y" in spec)

apply (case_tac "x = y", simp)
apply (simp only: to_distance_rs)
apply (simp add: distance_iff)
apply (insert gSKIP_to_Tick_notin_traces)
apply (frule_tac x="P" in spec)
apply (drule_tac x="P" in spec)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
apply (simp)
apply (fold to_distance_rs)
apply (simp add: gSKIP_map_alpha_traces_Seq_compo_lm)
done

(*--------------------------------*
 |          Depth_rest            |
 *--------------------------------*)

(*** rest_domT (equal) ***)

lemma Depth_rest_rest_domT:
   "traces P M1 .|. n = traces Q M2 .|. n
    ==> traces (P |. m) M1 .|. n = traces (Q |. m) M2 .|. n"
apply (simp add: traces.simps)
apply (simp add: min_rs)
apply (rule rest_equal_preserve)
apply (simp)
apply (simp add: min_def)
done

(*** distT lemma ***)

lemma Depth_rest_distT:
     "distance(traces (P |. m) M1, traces (Q |. m) M2) <=
      distance(traces P M1, traces Q M2)"
apply (simp only: to_distance_rs)
apply (rule rest_distance_subset)
by (auto intro: Depth_rest_rest_domT)

(*** map_alphaT lemma ***)

lemma map_alpha_traces_Depth_rest_lm:
  "distance(traces P M1, traces Q M2) <= alpha * distance (x1, x2)
    ==> distance(traces (P |. m) M1, traces (Q |. m) M2)
     <= alpha * distance(x1, x2)"
apply (insert Depth_rest_distT[of P m M1 Q M2])
by (simp)

(*** map_alpha ***)

lemma map_alpha_traces_Depth_rest:
 "map_alpha (traces P) alpha
  ==> map_alpha (traces (P |. m)) alpha"
apply (simp add: map_alpha_def)
apply (intro allI)
apply (erule conjE)
apply (drule_tac x="x" in spec)
apply (drule_tac x="y" in spec)
by (simp add: map_alpha_traces_Depth_rest_lm)

(*** non_expanding ***)

lemma non_expanding_traces_Depth_rest:
 "non_expanding (traces P)
  ==> non_expanding (traces (P |. m))"
by (simp add: non_expanding_def map_alpha_traces_Depth_rest)

(*** contraction_alpha ***)

lemma contraction_alpha_traces_Depth_rest:
 "contraction_alpha (traces P) alpha
  ==> contraction_alpha (traces (P |. m)) alpha"
by (simp add: contraction_alpha_def map_alpha_traces_Depth_rest)

(*--------------------------------*
 |            variable            |
 *--------------------------------*)

(*** non_expanding ***)

lemma non_expanding_traces_variable:
   "non_expanding (traces ($p))"
apply (simp add: traces_iff)
apply (simp add: non_expanding_prod_variable)
done

(*--------------------------------*
 |            Procfun             |
 *--------------------------------*)

(*****************************************************************
 |                         non_expanding                         |
 *****************************************************************)

lemma non_expanding_traces_lm:
  "noHide P --> non_expanding (traces P)"
apply (induct_tac P)
apply (simp add: non_expanding_traces_STOP)
apply (simp add: non_expanding_traces_SKIP)
apply (simp add: non_expanding_traces_DIV)
apply (simp add: non_expanding_traces_Act_prefix)
apply (simp add: non_expanding_traces_Ext_pre_choice)
apply (simp add: non_expanding_traces_Ext_choice)
apply (simp add: non_expanding_traces_Int_choice)
apply (simp add: non_expanding_traces_Rep_int_choice)
apply (simp add: non_expanding_traces_IF)
apply (simp add: non_expanding_traces_Parallel)

(* hiding --> const *)
apply (intro impI)
(* apply (subgoal_tac "EX T. (traces (proc -- fun) = (%M. T))") isabelle 2011 *)
(* apply (subgoal_tac "EX T. (traces (proc -- set) = (%M. T))") *)
apply (subgoal_tac "EX T. (traces (x1 -- x2) = (%M. T))")
apply (erule exE)
apply (simp)
apply (simp add: non_expanding_Constant)
apply (rule traces_noPN_Constant)
apply (simp)

apply (simp add: non_expanding_traces_Renaming)
apply (simp add: non_expanding_traces_Seq_compo)

(* Depth_res *)
apply (simp add: non_expanding_traces_Depth_rest)
apply (simp add: traces_iff)
apply (simp add: zero_rs_domT)
apply (simp add: non_expanding_Constant)

apply (simp add: non_expanding_traces_variable)
done

lemma non_expanding_traces:
  "noHide P ==> non_expanding (traces P)"
apply (simp add: non_expanding_traces_lm)
done

(*=============================================================*
 |                          [[P]]Tf                            |
 *=============================================================*)

lemma non_expanding_semTf:
  "noHide P ==> non_expanding [[P]]Tf"
apply (simp add: semTf_def)
apply (simp add: non_expanding_traces)
done

(*=============================================================*
 |                         [[P]]Tfun                           |
 *=============================================================*)

lemma non_expanding_semTfun:
  "noHidefun Pf ==> non_expanding [[Pf]]Tfun"
apply (simp add: noHidefun_def)
apply (simp add: prod_non_expand)
apply (simp add: semTfun_def)
apply (simp add: proj_fun_def)
apply (simp add: comp_def)
apply (simp add: non_expanding_semTf)
done

(*****************************************************************
 |                         contraction                           |
 *****************************************************************)

lemma contraction_alpha_traces_lm:
  "guarded P --> contraction_alpha (traces P) (1/2)"
apply (induct_tac P)
apply (simp add: contraction_alpha_traces_STOP)
apply (simp add: contraction_alpha_traces_SKIP)
apply (simp add: contraction_alpha_traces_DIV)
apply (simp add: contraction_half_traces_Act_prefix
                 non_expanding_traces)
apply (simp add: contraction_half_traces_Ext_pre_choice
                 non_expanding_traces)
apply (simp add: contraction_alpha_traces_Ext_choice)
apply (simp add: contraction_alpha_traces_Int_choice)
apply (simp add: contraction_alpha_traces_Rep_int_choice)
apply (simp add: contraction_alpha_traces_IF)
apply (simp add: contraction_alpha_traces_Parallel)

(* hiding --> const *)
apply (intro impI)
(* apply (subgoal_tac "EX T. (traces (proc -- fun) = (%M. T))") isabelle 2011 *)
(* apply (subgoal_tac "EX T. (traces (proc -- set) = (%M. T))") *)
apply (subgoal_tac "EX T. (traces (x1 -- x2) = (%M. T))")
apply (erule exE)
apply (simp add: contraction_alpha_Constant)
apply (rule traces_noPN_Constant)
apply (simp)

apply (simp add: contraction_alpha_traces_Renaming)

(* Seq_compo *)
apply (simp)
apply (intro conjI impI)
apply (simp add: gSKIP_contraction_half_traces_Seq_compo
                 non_expanding_traces)
apply (simp add: contraction_alpha_traces_Seq_compo)

(* Depth_res *)
apply (simp add: contraction_alpha_traces_Depth_rest)
apply (simp add: traces_iff)
apply (simp add: zero_rs_domT)
apply (simp add: contraction_alpha_Constant)

apply (simp)
done

lemma contraction_alpha_traces:
  "guarded P ==> contraction_alpha (traces P) (1/2)"
by (simp add: contraction_alpha_traces_lm)

(*=============================================================*
 |                          [[P]]Tf                            |
 *=============================================================*)

lemma contraction_alpha_semTf:
  "guarded P ==> contraction_alpha [[P]]Tf (1/2)"
apply (simp add: semTf_def)
apply (simp add: contraction_alpha_traces)
done

(*=============================================================*
 |                         [[P]]Tfun                           |
 *=============================================================*)

lemma contraction_alpha_semTfun:
  "guardedfun Pf ==> contraction_alpha [[Pf]]Tfun (1/2)"
apply (simp add: guardedfun_def)
apply (simp add: prod_contra_alpha)
apply (simp add: semTfun_def)
apply (simp add: proj_fun_def)
apply (simp add: comp_def)
apply (simp add: contraction_alpha_semTf)
done

(*=============================================================*
 |                        contraction                          |
 *=============================================================*)

lemma contraction_semTfun:
  "guardedfun Pf ==> contraction [[Pf]]Tfun"
apply (simp add: contraction_def)
apply (rule_tac x="1/2" in exI)
apply (simp add:contraction_alpha_semTfun)
done

end


section \<open>Arrays with Dynamic Resizing\<close>
theory Dynamic_Array
imports "IICF_List" "Array_Blit" "IICF_Array" "IICF_Map"
begin

(* TODO: Move to Array_Blit *)
definition "array_grow' a n x \<equiv> do {
  l\<leftarrow>Array.len a;
  a'\<leftarrow>Array.new (l+n) x;
  blit a 0 a' 0 l;
  return a'
}"

lemma array_grow'_rule[sep_heap_rules]:
  shows "
    < a\<mapsto>\<^sub>ala >
      array_grow' a n x
    <\<lambda>a'. a'\<mapsto>\<^sub>a (la @ replicate n x)>\<^sub>t"
  unfolding array_grow'_def
  by sep_auto

(* TODO: Move to IICF_List *)
sepref_decl_op list_grow:
  "\<lambda>xs n x. xs@replicate n x" :: "\<langle>A\<rangle>list_rel \<rightarrow> nat_rel \<rightarrow> A \<rightarrow> \<langle>A\<rangle>list_rel" .

definition "nff_\<alpha> dflt l i \<equiv> if i<length l then l!i else dflt"
definition "is_nff dflt f a \<equiv> \<exists>\<^sub>Al. a \<mapsto>\<^sub>a l * \<up>(f = nff_\<alpha> dflt l)"

definition [code_unfold]: "dyn_array_new_sz dflt n \<equiv> Array.new n dflt"

lemma dyn_array_new_sz_rl[sep_heap_rules]:
  "<emp> dyn_array_new_sz dflt n <\<lambda>r. is_nff dflt (\<lambda>_. dflt) r>"
  unfolding dyn_array_new_sz_def is_nff_def nff_\<alpha>_def
  by sep_auto

definition "array_get_dyn dflt a i \<equiv> do {
  (*nth_oo dflt a i*)
  l \<leftarrow> Array.len a;
  if i<l then Array.nth a i else return dflt
  }"

lemma array_get_dyn_rule[sep_heap_rules]: "
  < is_nff dflt f a >
    array_get_dyn dflt a i
  < \<lambda>r. is_nff dflt f a * \<up>(r = f i) >"
  unfolding array_get_dyn_def nth_oo_def
  by (sep_auto simp: nff_\<alpha>_def is_nff_def)

definition "array_set_dyn dflt a i v \<equiv> do {
  l \<leftarrow> Array.len a;
  if i<l then
    Array.upd i v a
  else do {
    let ns = max (2*l) (i+1);
    a \<leftarrow> array_grow a ns dflt;
    Array.upd i v a
  }
}"

lemma nff_\<alpha>_upd: "\<lbrakk>i < length l\<rbrakk> \<Longrightarrow> nff_\<alpha> dflt (l[i := v]) = (nff_\<alpha> dflt l)(i := v)"
  by (auto simp: nff_\<alpha>_def)

lemma array_set_dyn_rule[sep_heap_rules]: "
  < is_nff dflt f a >
    array_set_dyn dflt a i v
  <\<lambda>r. is_nff dflt (f(i:=v)) r >\<^sub>t
  "
  unfolding array_set_dyn_def is_nff_def upd_oo_def
  by (sep_auto simp: nff_\<alpha>_upd nff_\<alpha>_append_default)

end


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

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

header "Expectation Transformers"

theory Transformers imports Expectations begin

text_raw {* \label{s:transformers} *}

type_synonym 's trans = "'s expect \<Rightarrow> 's expect"

text {* Transformers are functions from expectations to expectations i.e. @{typ "('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow>
real"}.

The set of \emph{healthy} transformers is the universe into which we place our semantic
interpretation of pGCL programs. In its standard presentation, the healthiness condition for pGCL
programs is \emph{sublinearity}, for demonic programs, and \emph{superlinearity} for angelic
programs. We extract a minimal core property, consisting of monotonicity, feasibility and scaling to
form our healthiness property, which holds across all programs. The additional components of
sublinearity are broken out separately, and shown later. The two reasons for this are firstly to
avoid the effort of establishing sub-(super-)linearity globally, and to allow us to define
primitives whose sublinearity, and indeed healthiness, depend on context.

Consider again the automaton of \autoref{f:automaton_1}. Here, the effect of executing the automaton
from its initial state ($a$) until it reaches some final state ($b$ or $c$) is to \emph{transform}
the expectation on final states ($P$), into one on initial states, giving the \emph{expected} value
of the function on termination. Here, the transformation is linear: $P_\text{prior}(a) = 0.7 *
P_\text{post}(b) + 0.3 * P_\text{post}(c)$, but this need not be the case.

\begin{figure}
\begin{center}
\mbox{
\xymatrix{
*++[o][F=]{b} & & *++[o][F=]{c} \\
 *++[o][F-]{a} \ar[u]^{0.7} \ar[urr]_(0.25){0.3}  & & *++[o][F-]{d} \ar[ull]^(0.25){0.5} \ar[u]_{0.5}\\
  & *++[o][F-]{e}\ar[ul] \ar[ur] \\
  & \ar[u]
}
}
\end{center}
\caption{\label{f:automaton_2}A nondeterministic-probabilistic automaton.}
\end{figure}

Consider the automaton of \autoref{f:automaton_2}. Here, we have extended that of
\autoref{f:automaton_1} with two additional states, $d$ and $e$, and a pair of silent (unlabelled)
transitions. From the initial state, $e$, this automaton is free to transition either to the
original starting state ($a$), and thence behave exactly as the previous automaton did, or to $d$,
which has the same set of available transitions, now with different probabilities. Where previously
we could state that the automaton would terminate in state $b$ with probability 0.7 (and in $c$ with
probability 0.3), this now depends on the outcome of the \emph{nondeterministic} transition from $e$
to either $a$ or $d$. The most we can now say is that we must reach $b$ with probability \emph{at
least} 0.5 (the minimum from either $a$ or $d$) and $c$ with at least probability 0.3. Note that
these probabilities do not sum to one (although the sum will still always be less than one). The
associated expectation transformer is now \emph{sub-}linear: $P_\text{prior}(e) = 0.5 *
P_\text{post}(b) + 0.3 * P_\text{post}(c)$. *}

text_raw {*
\begin{figure}
\begin{center}
\mbox{
\xymatrix{
*++[o][F=]{b} & & *++[o][F=]{c} \\
      *++[o][F-]{a} \ar@(dl,ul)[] \ar[u]^{0.5} \ar[urr]_{0.3}
  & & *++[o][F-]{d} \ar@(dr,ur)[] \\
  & *++[o][F-]{e}\ar[ul]^{0.5} \ar[ur]_{0.5} \\
  & \ar[u]
}
}
\end{center}
\caption{\label{f:automaton_3}A diverging automaton.}
\end{figure}
*}

text {*
Finally, \autoref{f:automaton_3} shows the other way in which strict sublinearity arises:
divergence.  This automaton transitions with probability 0.5 to state $d$, from which it never
escapes.  Once there, the probability of reaching any terminating state is zero, and thus the
probabilty of terminating from the initial state ($e$) is no higher than 0.5.  If it instead
takes the edge to state $a$, we again see a self loop, and thus in theory an infinite trace.  In
this case, however, every time the automaton reaches state $a$, with probability $0.5 + 0.3 = 0.8$,
it transitions to a terminating state.  An infinite trace of transitions $a \rightarrow a
\rightarrow \ldots$ thus has probability 0, and the automaton terminates with probability 1.  We
formalise such probabilistic termination arguments in \autoref{s:termination}.

Having reached $a$, the automaton will proceed to $b$ with probability $0.5 * (1 / (0.5 + 0.3)) =
0.625$, and to $c$ with probability $0.375$.  As $a$ is in turn reached half the time, the final
probability of ending in $b$ is $0.3125$, and in $c$, $0.1875$, which sum to only $0.5$.  The
remaining probability is that the automaton diverges via $d$.  We view nondeterminism and
divergence demonically: we take the \emph{least} probability of reaching a given final state, and
use it to calculate the expectation.  Thus for this automaton, $P_\text{prior}(e) = 0.3125 *
P_\text{post}(b) + 0.1875 * P_\text{post}(c)$.  The end result is the same as for nondeterminism:
a sublinear transformation (the weights sum to less than one).  The two outcomes are thus unified
in the semantic interpretation, although as we will establish in \autoref{s:prog_determ}, the two
have slightly different algebraic properties.

This pattern holds for all pGCL programs: probabilistic choices are always linear, while struct
sublinearity is introduced both nondeterminism and divergence.

Healthiness, again, is the combination of three properties: feasibility, monotonicity and
scaling. Feasibility requires that a transformer take non-negative expectations to non-negative
expectations, and preserve bounds. Thus, starting with an expectation bounded between 0 and some
bound, $b$, after applying any number of feasible transformers, the result will still be bounded
between 0 and $b$. This closure property allows us to treat expectations almost as a complete
lattice. Specifically, for any $b$, the set of expectations bounded by $b$ is a complete lattice
($\bot = (\lambda s. 0)$, $\top = (\lambda s. b)$), and is closed under the action of feasible
transformers, including $\sqcap$ and $\sqcup$, which are themselves feasible. We are thus able to
define both least and greatest fixed points on this set, and thus give semantics to recursive
programs built from feasible components. *}

subsection {* Comparing Transformers *}

text {* Transformers are compared pointwise, but only on @{term sound} expectations. From the
preorder so generated, we define equivalence by antisymmetry, giving a partial order. *}

definition
  le_trans :: "'s trans \<Rightarrow> 's trans \<Rightarrow> bool"
where
  "le_trans t u \<equiv> \<forall>P. sound P \<longrightarrow> t P \<le> u P"

text {* We also need to define relations restricted to @{term unitary} transformers, for the
liberal (wlp) semantics. *}

definition
  le_utrans :: "'s trans \<Rightarrow> 's trans \<Rightarrow> bool"
where
  "le_utrans t u \<longleftrightarrow> (\<forall>P. unitary P \<longrightarrow> t P \<le> u P)"

lemma le_transI[intro]:
  "\<lbrakk> \<And>P. sound P \<Longrightarrow> t P \<le> u P \<rbrakk> \<Longrightarrow> le_trans t u"
  by(simp add:le_trans_def)

lemma le_utransI[intro]:
  "\<lbrakk> \<And>P. unitary P \<Longrightarrow> t P \<le> u P \<rbrakk> \<Longrightarrow> le_utrans t u"
  by(simp add:le_utrans_def)

lemma  le_transD[dest]:
  "\<lbrakk> le_trans t u; sound P \<rbrakk> \<Longrightarrow> t P \<le> u P"
  by(simp add:le_trans_def)

lemma le_utransD[dest]:
  "\<lbrakk> le_utrans t u; unitary P \<rbrakk> \<Longrightarrow> t P \<le> u P"
  by(simp add:le_utrans_def)

lemma le_trans_refl[iff]:
  "le_trans x x"
  by(simp add:le_trans_def)

lemma le_utrans_refl[iff]:
  "le_utrans x x"
  by(simp add:le_utrans_def)

lemma le_trans_le_utrans[dest]:
  "le_trans t u \<Longrightarrow> le_utrans t u"
  unfolding le_trans_def le_utrans_def by(auto)

definition
  l_trans :: "'s trans \<Rightarrow> 's trans \<Rightarrow> bool"
where
  "l_trans t u \<longleftrightarrow> le_trans t u \<and> \<not> le_trans u t"

text {* Transformer equivalence is induced by comparison: *}

definition
  equiv_trans :: "'s trans \<Rightarrow> 's trans \<Rightarrow> bool"
where
  "equiv_trans t u \<longleftrightarrow> le_trans t u \<and> le_trans u t"

definition
  equiv_utrans :: "'s trans \<Rightarrow> 's trans \<Rightarrow> bool"
where
  "equiv_utrans t u \<longleftrightarrow> le_utrans t u \<and> le_utrans u t"

lemma equiv_transI[intro]:
  "\<lbrakk> \<And>P. sound P \<Longrightarrow> t P = u P \<rbrakk> \<Longrightarrow> equiv_trans t u"
  unfolding equiv_trans_def by(force)

lemma equiv_utransI[intro]:
  "\<lbrakk> \<And>P. sound P \<Longrightarrow> t P = u P \<rbrakk> \<Longrightarrow> equiv_utrans t u"
  unfolding equiv_utrans_def by(force)

lemma equiv_transD[dest]:
  "\<lbrakk> equiv_trans t u; sound P \<rbrakk> \<Longrightarrow> t P = u P"
  unfolding equiv_trans_def by(blast intro:antisym)

lemma equiv_utransD[dest]:
  "\<lbrakk> equiv_utrans t u; unitary P \<rbrakk> \<Longrightarrow> t P = u P"
  unfolding equiv_utrans_def by(blast intro:antisym)

lemma equiv_trans_refl[iff]:
  "equiv_trans t t"
  by(blast)

lemma equiv_utrans_refl[iff]:
  "equiv_utrans t t"
  by(blast)

lemma le_trans_antisym:
  "\<lbrakk> le_trans x y; le_trans y x \<rbrakk> \<Longrightarrow> equiv_trans x y"
  unfolding equiv_trans_def by(simp)

lemma le_utrans_antisym:
  "\<lbrakk> le_utrans x y; le_utrans y x \<rbrakk> \<Longrightarrow> equiv_utrans x y"
  unfolding equiv_utrans_def by(simp)

lemma equiv_trans_comm[ac_simps]:
  "equiv_trans t u \<longleftrightarrow> equiv_trans u t"
  unfolding equiv_trans_def by(blast)

lemma equiv_utrans_comm[ac_simps]:
  "equiv_utrans t u \<longleftrightarrow> equiv_utrans u t"
  unfolding equiv_utrans_def by(blast)

lemma equiv_imp_le[intro]:
  "equiv_trans t u \<Longrightarrow> le_trans t u"
  unfolding equiv_trans_def by(clarify)

lemma equivu_imp_le[intro]:
  "equiv_utrans t u \<Longrightarrow> le_utrans t u"
  unfolding equiv_utrans_def by(clarify)

lemma equiv_imp_le_alt:
  "equiv_trans t u \<Longrightarrow> le_trans u t"
  by(force simp:ac_simps)

lemma equiv_uimp_le_alt:
  "equiv_utrans t u \<Longrightarrow> le_utrans u t"
  by(force simp:ac_simps)

lemma le_trans_equiv_rsp[simp]:
  "equiv_trans t u \<Longrightarrow> le_trans t v \<longleftrightarrow> le_trans u v"
  unfolding equiv_trans_def by(blast intro:le_trans_trans)

lemma le_utrans_equiv_rsp[simp]:
  "equiv_utrans t u \<Longrightarrow> le_utrans t v \<longleftrightarrow> le_utrans u v"
  unfolding equiv_utrans_def by(blast intro:le_utrans_trans)

lemma equiv_trans_le_trans[trans]:
  "\<lbrakk> equiv_trans t u; le_trans u v \<rbrakk> \<Longrightarrow> le_trans t v"
  by(simp)

lemma equiv_utrans_le_utrans[trans]:
  "\<lbrakk> equiv_utrans t u; le_utrans u v \<rbrakk> \<Longrightarrow> le_utrans t v"
  by(simp)

lemma le_trans_equiv_rsp_right[simp]:
  "equiv_trans t u \<Longrightarrow> le_trans v t \<longleftrightarrow> le_trans v u"
  unfolding equiv_trans_def by(blast intro:le_trans_trans)

lemma le_utrans_equiv_rsp_right[simp]:
  "equiv_utrans t u \<Longrightarrow> le_utrans v t \<longleftrightarrow> le_utrans v u"
  unfolding equiv_utrans_def by(blast intro:le_utrans_trans)

lemma le_trans_equiv_trans[trans]:
  "\<lbrakk> le_trans t u; equiv_trans u v \<rbrakk> \<Longrightarrow> le_trans t v"
  by(simp)

lemma le_utrans_equiv_utrans[trans]:
  "\<lbrakk> le_utrans t u; equiv_utrans u v \<rbrakk> \<Longrightarrow> le_utrans t v"
  by(simp)

lemma equiv_trans_trans[trans]:
  assumes xy: "equiv_trans x y"
      and yz: "equiv_trans y z"
  shows "equiv_trans x z"
proof(rule le_trans_antisym)
  from xy have "le_trans x y" by(blast)
  also from yz have "le_trans y z" by(blast)
  finally show "le_trans x z" .
  from yz have "le_trans z y" by(force simp:ac_simps)
  also from xy have "le_trans y x" by(force simp:ac_simps)
  finally show "le_trans z x" .
qed

lemma equiv_utrans_trans[trans]:
  assumes xy: "equiv_utrans x y"
      and yz: "equiv_utrans y z"
  shows "equiv_utrans x z"
proof(rule le_utrans_antisym)
  from xy have "le_utrans x y" by(blast)
  also from yz have "le_utrans y z" by(blast)
  finally show "le_utrans x z" .
  from yz have "le_utrans z y" by(force simp:ac_simps)
  also from xy have "le_utrans y x" by(force simp:ac_simps)
  finally show "le_utrans z x" .
qed

lemma equiv_trans_equiv_utrans[dest]:
  "equiv_trans t u \<Longrightarrow> equiv_utrans t u"
  by(auto)

subsection {* Healthy Transformers *}

subsubsection {* Feasibility *}

definition feasible :: "(('a \<Rightarrow> real) \<Rightarrow> ('a \<Rightarrow> real)) \<Rightarrow> bool"
where     "feasible t \<longleftrightarrow> (\<forall>P b. bounded_by b P \<and> nneg P \<longrightarrow>
                               bounded_by b (t P) \<and> nneg (t P))"

text {* A @{term feasible} transformer preserves non-negativity, and bounds. A @{term feasible}
transformer always takes its argument `closer to 0' (or leaves it where it is). Note that any
particular value of the expectation may increase, but no element of the new expectation may exceed
any bound on the old. This is thus a relatively weak condition. *}

lemma feasibleI[intro]:
  "\<lbrakk> \<And>b P. \<lbrakk> bounded_by b P; nneg P \<rbrakk> \<Longrightarrow> bounded_by b (t P);
     \<And>b P. \<lbrakk> bounded_by b P; nneg P \<rbrakk> \<Longrightarrow> nneg (t P) \<rbrakk> \<Longrightarrow> feasible t"
  by(force simp:feasible_def)

lemma feasible_boundedD[dest]:
  "\<lbrakk> feasible t; bounded_by b P; nneg P \<rbrakk> \<Longrightarrow> bounded_by b (t P)"
  by(simp add:feasible_def)

lemma feasible_nnegD[dest]:
  "\<lbrakk> feasible t; bounded_by b P; nneg P \<rbrakk> \<Longrightarrow> nneg (t P)"
  by(simp add:feasible_def)

lemma feasible_sound[dest]:
  "\<lbrakk> feasible t; sound P \<rbrakk> \<Longrightarrow> sound (t P)"
  by(rule soundI, unfold sound_def, (blast)+)

lemma feasible_pr_0[simp]:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes ft: "feasible t"
  shows "t (\<lambda>x. 0) = (\<lambda>x. 0)"
proof(rule ext, rule antisym)
  fix s

  have "bounded_by 0 (\<lambda>_::'s. 0::real)" by(blast)
  with ft have "bounded_by 0 (t (\<lambda>_. 0))" by(blast)
  thus "t (\<lambda>_. 0) s \<le> 0" by(blast)

  have "nneg (\<lambda>_::'s. 0::real)" by(blast)
  with ft have "nneg (t (\<lambda>_. 0))" by(blast)
  thus "0 \<le> t (\<lambda>_. 0) s" by(blast)
qed

lemma feasible_id:
  "feasible (\<lambda>x. x)"
  unfolding feasible_def by(blast)

lemma feasible_bounded_by[dest]:
  "\<lbrakk> feasible t; sound P; bounded_by b P \<rbrakk> \<Longrightarrow> bounded_by b (t P)"
  by(auto)

lemma feasible_fixes_top:
  "feasible t \<Longrightarrow> t (\<lambda>s. 1) \<le> (\<lambda>s. (1::real))"
  by(drule bounded_byD2[OF feasible_bounded_by], auto)

lemma feasible_fixes_bot:
  assumes ft: "feasible t"
  shows "t (\<lambda>s. 0) = (\<lambda>s. 0)"
proof(rule antisym)
  have sb: "sound (\<lambda>s. 0)" by(auto)
  with ft show "(\<lambda>s. 0) \<le> t (\<lambda>s. 0)" by(auto)
  thm bound_of_const
  from sb have "bounded_by (bound_of (\<lambda>s. 0::real)) (\<lambda>s. 0)" by(auto)
  hence "bounded_by 0 (\<lambda>s. 0::real)" by(simp add:bound_of_const)
  with ft have "bounded_by 0 (t (\<lambda>s. 0))" by(auto)
  thus "t (\<lambda>s. 0) \<le> (\<lambda>s. 0)" by(auto)
qed

lemma feasible_unitaryD[dest]:
  assumes ft: "feasible t" and uP: "unitary P"
  shows "unitary (t P)"
proof(rule unitaryI)
  from uP have "sound P" by(auto)
  with ft show "sound (t P)" by(auto)
  from assms show "bounded_by 1 (t P)" by(auto)
qed

subsubsection {* Monotonicity *}

definition
  mono_trans :: "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "mono_trans t \<equiv> \<forall>P Q. (sound P \<and> sound Q \<and> P \<le> Q) \<longrightarrow> t P \<le> t Q"

text {* Monotonicity allows us to compose transformers, and thus model sequential computation.
Recall the definition of predicate entailment (\autoref{s:entailment}) as less-than-or-equal. The
statement @{term "Q \<tturnstile> t R"} means that @{term Q} is everywhere below @{term "t R"}. For standard
expectations (\autoref{s:standard}), this simply means that @{term Q} \emph{implies} @{term "t R"},
the \emph{weakest precondition} of @{term R} under @{term t}.

Given another, monotonic, transformer @{term u}, we have that @{term "u Q \<tturnstile> u (t R)"}, or that the
weakest precondition of @{term Q} under @{term u} entails that of @{term R} under the composition
@{term "u o t"}.  If we additionally know that @{term "P \<tturnstile> u Q"}, then by transitivity we have
@{term "P \<tturnstile> u (t R)"}.  We thus derive a probabilistic form of the standard rule for sequential
composition: @{term "\<lbrakk> mono_trans t; P \<tturnstile> u Q; Q \<tturnstile> t R \<rbrakk> \<Longrightarrow> P \<tturnstile> u (t R)"}.
*}

lemma mono_transI[intro]:
  "\<lbrakk> \<And>P Q. \<lbrakk> sound P; sound Q; P \<le> Q \<rbrakk> \<Longrightarrow>  t P \<le> t Q \<rbrakk> \<Longrightarrow> mono_trans t"
  by(simp add:mono_trans_def)

lemma mono_transD[dest]:
  "\<lbrakk> mono_trans t; sound P; sound Q; P \<le> Q \<rbrakk> \<Longrightarrow> t P \<le> t Q"
  by(simp add:mono_trans_def)

subsubsection {* Scaling *}
text_raw {* \label{s:scaling} *}

text {* A healthy transformer commutes with scaling by a non-negative constant. *}

definition
  scaling :: "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "scaling t \<equiv> \<forall>P c x. sound P \<and> 0 \<le> c \<longrightarrow> c * t P x = t (\<lambda>x. c * P x) x"

text {* The @{term scaling} and feasibility properties together allow us to treat transformers as a
complete lattice, when operating on bounded expectations. The action of a transformer on such a
bounded expectation is completely determined by its action on \emph{unitary} expectations (those
bounded by 1): @{term "t P s = bound_of P * t (\<lambda>s. P s / bound_of P) s"}. Feasibility in turn
ensures that the lattice of unitary expectations is closed under the action of a healthy
transformer. We take advantage of this fact in \autoref{s:induction}, in order to define the fixed
points of healthy transformers. *}

lemma scalingI[intro]:
  "\<lbrakk> \<And>P c x. \<lbrakk> sound P; 0 \<le> c \<rbrakk> \<Longrightarrow> c * t P x = t (\<lambda>x. c * P x) x \<rbrakk> \<Longrightarrow> scaling t"
  by(simp add:scaling_def)

lemma scalingD[dest]:
  "\<lbrakk> scaling t; sound P; 0 \<le> c \<rbrakk>  \<Longrightarrow> c * t P x = t (\<lambda>x. c * P x) x"
  by(simp add:scaling_def)

lemma right_scalingD:
  assumes st: "scaling t"
      and sP: "sound P"
      and nnc: "0 \<le> c"
  shows "t P s * c = t (\<lambda>s. P s * c) s"
proof -
  have "t P s * c = c * t P s" by(simp add:algebra_simps)
  also from assms have "... = t (\<lambda>s. c * P s) s" by(rule scalingD)
  also have "... = t (\<lambda>s. P s * c) s" by(simp add:algebra_simps)
  finally show ?thesis .
qed

subsubsection {* Healthiness *}

text {* Healthy transformers are feasible and monotonic, and respect scaling *}

definition
  healthy :: "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "healthy t \<longleftrightarrow> feasible t \<and> mono_trans t \<and> scaling t"

lemma healthyI[intro]:
  "\<lbrakk> feasible t; mono_trans t; scaling t \<rbrakk> \<Longrightarrow> healthy t"
  by(simp add:healthy_def)

lemmas healthy_parts = healthyI[OF feasibleI mono_transI scalingI]

lemma healthy_monoD[dest]:
  "healthy t \<Longrightarrow> mono_trans t"
  by(simp add:healthy_def)

lemmas healthy_monoD2 = mono_transD[OF healthy_monoD]

lemma healthy_feasibleD[dest]:
  "healthy t \<Longrightarrow> feasible t"
  by(simp add:healthy_def)

lemma healthy_scalingD[dest]:
  "healthy t \<Longrightarrow> scaling t"
  by(simp add:healthy_def)

lemma healthy_bounded_byD[intro]:
  "\<lbrakk> healthy t; bounded_by b P; nneg P \<rbrakk> \<Longrightarrow> bounded_by b (t P)"
   by(blast)

lemma healthy_bounded_byD2:
  "\<lbrakk> healthy t; bounded_by b P; sound P \<rbrakk> \<Longrightarrow> bounded_by b (t P)"
  by(blast)

lemma healthy_boundedD[dest,simp]:
  "\<lbrakk> healthy t; sound P \<rbrakk> \<Longrightarrow> bounded (t P)"
  by(blast)

lemma healthy_nnegD[dest,simp]:
  "\<lbrakk> healthy t; sound P \<rbrakk> \<Longrightarrow> nneg (t P)"
  by(blast intro!:feasible_nnegD)

lemma healthy_nnegD2[dest,simp]:
  "\<lbrakk> healthy t; bounded_by b P; nneg P \<rbrakk> \<Longrightarrow> nneg (t P)"
  by(blast)

lemma healthy_sound[intro]:
  "\<lbrakk> healthy t; sound P \<rbrakk> \<Longrightarrow> sound (t P)"
  by(rule soundI, blast, blast intro:feasible_nnegD)

lemma healthy_unitary[intro]:
  "\<lbrakk> healthy t; unitary P \<rbrakk> \<Longrightarrow> unitary (t P)"
  by(blast intro!:unitaryI dest:unitary_bound healthy_bounded_byD)

lemma healthy_id[simp,intro!]:
  "healthy id"
  by(simp add:healthyI feasibleI mono_transI scalingI)

lemmas healthy_fixes_bot = feasible_fixes_bot[OF healthy_feasibleD]

text {* Some additional results on @{term le_trans}, specific to
@{term healthy} transformers. *}

lemma le_trans_bot[intro,simp]:
  "healthy t \<Longrightarrow> le_trans (\<lambda>P s. 0) t"
  by(blast intro:le_funI)

lemma le_trans_top[intro,simp]:
  "healthy t \<Longrightarrow> le_trans t (\<lambda>P s. bound_of P)"
  by(blast intro!:le_transI[OF le_funI])

lemma healthy_pr_bot[simp]:
  "healthy t \<Longrightarrow> t (\<lambda>s. 0) = (\<lambda>s. 0)"
  by(blast intro:feasible_pr_0)

text {* The first significant result is that healthiness is preserved by equivalence: *}

lemma healthy_equivI:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real" and u
  assumes equiv:   "equiv_trans t u"
      and healthy: "healthy t"
  shows "healthy u"
proof
  have le_t_u: "le_trans t u" by(blast intro:equiv)
  have le_u_t: "le_trans u t" by(simp add:equiv_imp_le ac_simps equiv)
  from equiv have eq_u_t: "equiv_trans u t" by(simp add:ac_simps)

  show "feasible u"
  proof
    fix b and P::"'s \<Rightarrow> real"
    assume bP: "bounded_by b P" and nP: "nneg P"
    hence sP: "sound P" by(blast)
    with healthy have "\<And>s. 0 \<le> t P s" by(blast)
    also from sP and le_t_u have "\<And>s. ... s \<le> u P s" by(blast)
    finally show "nneg (u P)" by(blast)

    from sP and le_u_t have "\<And>s. u P s \<le> t P s" by(blast)
    also from healthy and sP and bP have "\<And>s. t P s \<le> b" by(blast)
    finally show "bounded_by b (u P)" by(blast)
  qed

  show "mono_trans u"
  proof
    fix P::"'s \<Rightarrow> real" and Q::"'s \<Rightarrow> real"
    assume sP: "sound P" and sQ: "sound Q"
       and le: "P \<tturnstile> Q"
    from sP and le_u_t have "u P \<tturnstile> t P" by(blast)
    also from sP and sQ and le and healthy have "t P \<tturnstile> t Q" by(blast)
    also from sQ and le_t_u have "t Q \<tturnstile> u Q" by(blast)
    finally show "u P \<tturnstile> u Q" .
  qed

  show "scaling u"
  proof
    fix P::"'s \<Rightarrow> real" and c::real and x::'s
    assume sound: "sound P"
       and pos:   "0 \<le> c"

    hence "bounded_by (c * bound_of P) (\<lambda>x. c * P x)"
      by(blast intro!:mult_left_mono dest!:less_imp_le)
    hence sc_bounded: "bounded (\<lambda>x. c * P x)"
      by(blast)
    moreover from sound and pos have sc_nneg: "nneg (\<lambda>x. c * P x)"
      by(blast intro:mult_nonneg_nonneg less_imp_le)
    ultimately have sc_sound: "sound (\<lambda>x. c * P x)" by(blast)

    show "c * u P x = u (\<lambda>x. c * P x) x"
    proof -
      from sound have "c * u P x = c * t P x"
        by(simp add:equiv_transD[OF eq_u_t])

      also have "... = t (\<lambda>x. c * P x) x"
        using healthy and sound and pos
        by(blast intro: scalingD)

      also from sc_sound and equiv have "... = u (\<lambda>x. c * P x) x"
        by(blast intro:fun_cong)

      finally show ?thesis .
    qed
  qed
qed

lemma healthy_equiv:
  "equiv_trans t u \<Longrightarrow> healthy t \<longleftrightarrow> healthy u"
  by(rule iffI, rule healthy_equivI, assumption+,
     simp add:healthy_equivI ac_simps)

lemma healthy_scale:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes ht: "healthy t" and nc: "0 \<le> c" and bc: "c \<le> 1"
  shows "healthy (\<lambda>P s. c * t P s)"
proof
  show "feasible (\<lambda>P s. c * t P s)"
  proof
    fix b and P::"'s \<Rightarrow> real"
    assume nnP: "nneg P" and bP: "bounded_by b P"

    from ht nnP bP have "\<And>s. t P s \<le> b" by(blast)
    with nc have "\<And>s. c * t P s \<le> c * b" by(blast intro:mult_left_mono)
    also {
      from nnP and bP have "0 \<le> b" by(auto)
      with bc have "c * b \<le> 1 * b" by(blast intro:mult_right_mono)
      hence "c * b \<le> b" by(simp)
    }
    finally show "bounded_by b (\<lambda>s. c * t P s)" by(blast)

    from ht nnP bP have "\<And>s. 0 \<le> t P s" by(blast)
    with nc have "\<And>s. 0 \<le> c * t P s" by(rule mult_nonneg_nonneg)
    thus "nneg (\<lambda>s. c * t P s)" by(blast)
  qed
  show "mono_trans (\<lambda>P s. c * t P s)"
  proof
    fix P::"'s \<Rightarrow> real" and Q
    assume sP: "sound P" and sQ: "sound Q" and le: "P \<tturnstile> Q"
    with ht have "\<And>s. t P s \<le> t Q s" by(auto intro:le_funD)
    with nc have "\<And>s. c * t P s \<le> c * t Q s"
      by(blast intro:mult_left_mono)
    thus "\<lambda>s. c * t P s \<tturnstile> \<lambda>s. c * t Q s" by(blast)
  qed
  from ht show "scaling (\<lambda>P s. c * t P s)"
    by(auto simp:scalingD healthy_scalingD ht)
qed

lemma healthy_top[iff]:
  "healthy (\<lambda>P s. bound_of P)"
  by(auto intro!:healthy_parts)

lemma healthy_bot[iff]:
  "healthy (\<lambda>P s. 0)"
  by(auto intro!:healthy_parts)

text {* This weaker healthiness condition is for the liberal (wlp) semantics. We only insist that
the transformer preserves \emph{unitarity} (bounded by 1), and drop scaling (it is unnecessary in
establishing the lattice structure here, unlike for the strict semantics). *}

definition
  nearly_healthy :: "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "nearly_healthy t \<longleftrightarrow> (\<forall>P. unitary P \<longrightarrow> unitary (t P)) \<and>
                        (\<forall>P Q. unitary P \<longrightarrow> unitary Q \<longrightarrow> P \<tturnstile> Q \<longrightarrow> t P \<tturnstile> t Q)"

lemma nearly_healthyI[intro]:
  "\<lbrakk> \<And>P. unitary P \<Longrightarrow> unitary (t P);
     \<And>P Q. \<lbrakk> unitary P; unitary Q; P \<tturnstile> Q \<rbrakk> \<Longrightarrow> t P \<tturnstile> t Q \<rbrakk> \<Longrightarrow> nearly_healthy t"
  by(simp add:nearly_healthy_def)

lemma nearly_healthy_monoD[dest]:
  "\<lbrakk> nearly_healthy t; P \<tturnstile> Q; unitary P; unitary Q \<rbrakk> \<Longrightarrow> t P \<tturnstile> t Q"
  by(simp add:nearly_healthy_def)

lemma nearly_healthy_unitaryD[dest]:
  "\<lbrakk> nearly_healthy t; unitary P \<rbrakk> \<Longrightarrow> unitary (t P)"
  by(simp add:nearly_healthy_def)

lemma healthy_nearly_healthy[dest]:
  assumes ht: "healthy t"
  shows "nearly_healthy t"
  by(intro nearly_healthyI, auto intro:mono_transD[OF healthy_monoD, OF ht] ht)

lemmas nearly_healthy_id[iff] =
  healthy_nearly_healthy[OF healthy_id, unfolded id_def]

subsection {* Sublinearity *}

text {* As already mentioned, the core healthiness property (aside from feasibility and continuity)
for transformers is \emph{sublinearity}: The transformation of a quasi-linear combination of sound
expectations is greater than the same combination applied to the transformation of the expectations
themselves. The term @{term "x \<ominus> y"} represents \emph{truncated subtraction} i.e. @{term "max (x-y)
0"} (see \autoref{s:trunc_sub}). *}

definition sublinear ::
  "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "sublinear t \<longleftrightarrow> (\<forall>a b c P Q s. (sound P \<and> sound Q \<and> 0 \<le> a \<and> 0 \<le> b \<and> 0 \<le> c) \<longrightarrow>
                  a * t P s + b * t Q s \<ominus> c
                  \<le> t (\<lambda>s'. a * P s' + b * Q s' \<ominus> c) s)"

lemma sublinearI[intro]:
  "\<lbrakk> \<And>a b c P Q s. \<lbrakk> sound P; sound Q; 0 \<le> a; 0 \<le> b; 0 \<le> c \<rbrakk> \<Longrightarrow>
     a * t P s + b * t Q s \<ominus> c \<le>
     t (\<lambda>s'. a * P s' + b * Q s' \<ominus> c) s \<rbrakk> \<Longrightarrow> sublinear t"
  by(simp add:sublinear_def)

lemma sublinearD[dest]:
  "\<lbrakk> sublinear t; sound P; sound Q; 0 \<le> a; 0 \<le> b; 0 \<le> c \<rbrakk> \<Longrightarrow>
   a * t P s + b * t Q s \<ominus> c \<le>
   t (\<lambda>s'. a * P s' + b * Q s' \<ominus> c) s"
  by(simp add:sublinear_def)

text {* It is easier to see the relevance of sublinearity by breaking it into several component
properties, as in the following sections. *}

subsubsection {* Sub-additivity *}
text_raw {* \label{s:subadd} *}

definition sub_add ::
  "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "sub_add t \<longleftrightarrow> (\<forall>P Q s. (sound P \<and> sound Q) \<longrightarrow>
                t P s + t Q s \<le> t (\<lambda>s'. P s' + Q s') s)"

text {*
\begin{figure}
\begin{center}
\begin{displaymath}
\begin{xy}
0;<1cm,0cm>:
(-0.25,0); (10,0) **\dir{-} *\dir{>},
(0,-0.25); (0,6) **\dir{-} *\dir{>},
(0.1,5.5)="Ps";  (9.9,1.5)="Pe"  **\dir{-} ?(0.1)+<0em,1em> *{P},
(0.1,4.0)="tPs"; (9.9,1.0)="tPe" **\dir{} ?(0.1)+<0em,1em> *{tP},
(0.1,0.5)="uPs"; (9.9,5.0)="uPe" **\dir{} ?(0.9)+<0em,1em> *{uP}
?!{"tPs";"tPe"}="inter";
    "tPs" **\dir{--}, "uPe" **\dir{--},
    "tPe" **\dir{-}, "uPs" **\dir{-} ?(0.5)+<0em,1.5em> *{Q=tP \sqcap uP},
(1,0)="x"; "x"-<0em,1em>*{x};
"x"; (1,6) **{}, ?!{"uPs";"uPe"}="uPx" *{\bullet} -<0em,1em>*{Q(x)},
(9,0)="y"; "y"-<0em,1em>*{y};
"y"; (9,6) **{}, ?!{"tPs";"tPe"}="tPy" *{\bullet} -<0em,1em>*{Q(y)},
"uPx"; "tPy" **\dir{.},
(5,0)="xy"; (5,6) **{},
    ?!{"tPs";"tPe"}="tPxy" *{\bullet} -<0em,1.5em>*{Q(\frac{x+y}{2})},
    ?!{"uPx";"tPy"}="tPuP" *{\bullet} -<0em,1em>*{\frac{Q(x)+Q(y)}{2}},
\end{xy}
\end{displaymath}
\end{center}
\caption{\label{f:subadd_plot}A graphical depiction of sub-additivity as convexity.}
\end{figure}
*}

text {* Sub-additivity, together with scaling (\autoref{s:scaling}) gives the \emph{linear} portion
of sublinearity. Together, these two properties are equivalent to \emph{convexity}, as
\autoref{f:subadd_plot} illustrates by analogy.

Here $P$ is an affine function (expectation) @{typ "real \<Rightarrow> real"}, restricted to some finite
interval. In practice the state space (the left-hand type) is typically discrete and
multi-dimensional, but on the reals we have a convenient geometrical intuition. The lines $tP$ and
$uP$ represent the effect of two healthy transformers (again affine). Neither monotonicity nor
scaling are represented, but both are feasible: Both lines are bounded above by the greatest value
of $P$.

The curve $Q$ is the pointwise minimum of $tP$ and $tQ$, written $tP \sqcap tQ$.  This is, not
coincidentally, the syntax for a binary nondeterministic choice in pGCL: The probability that some
property is established by the choice between programs $a$ and $b$ cannot be guaranteed to be any
higher than either the probability under $a$, or that under $b$.

The original curve, $P$, is trivially convex---it is linear.  Also, both $t$ and $u$, and the
operator $\sqcap$ preserve convexity.  A probabilistic choice will also preserve it.  The
preservation of convexity is a property of sub-additive transformers that respect scaling.  Note
the form of the definition of convexity:
\begin{displaymath}
\forall x,y. \frac{Q(x) + Q(y)}{2} \le Q(\frac{x+y}{2})
\end{displaymath}
Were we to replace $Q$ by some sub-additive transformer $v$, and $x$ and $y$ by expectations $R$
and $S$, the equivalent expression:
\begin{displaymath}
\frac{vR + vS}{2} \le v(\frac{R+S}{2})
\end{displaymath}
Can be rewritten, using scaling, to:
\begin{displaymath}
\frac{1}{2}(vR + vS) \le \frac{1}{2}v(R+S)
\end{displaymath}
Which holds everywhere exactly when $v$ is sub-additive i.e.:
\begin{displaymath}
vR + vS \le v(R+S)
\end{displaymath}
*}

lemma sub_addI[intro]:
  "\<lbrakk> \<And>P Q s. \<lbrakk> sound P; sound Q \<rbrakk> \<Longrightarrow>
             t P s + t Q s \<le> t (\<lambda>s'. P s' + Q s') s \<rbrakk> \<Longrightarrow> sub_add t"
  by(simp add:sub_add_def)

lemma sub_addI2:
  "\<lbrakk>\<And>P Q. \<lbrakk> sound P; sound Q \<rbrakk> \<Longrightarrow>
          \<lambda>s. t P s + t Q s \<tturnstile> t (\<lambda>s. P s + Q s)\<rbrakk> \<Longrightarrow>
   sub_add t"
  by(auto)

lemma sub_addD[dest]:
  "\<lbrakk> sub_add t; sound P; sound Q \<rbrakk> \<Longrightarrow> t P s + t Q s \<le> t (\<lambda>s'. P s' + Q s') s"
  by(simp add:sub_add_def)

lemma equiv_sub_add:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes eq: "equiv_trans t u"
      and sa: "sub_add t"
  shows "sub_add u"
proof
  fix P::"'s \<Rightarrow> real" and Q::"'s \<Rightarrow> real" and s::"'s"
  assume sP: "sound P" and sQ: "sound Q"

  with eq have "u P s + u Q s = t P s + t Q s"
    by(simp add:equiv_transD)
  also from sP sQ sa have "t P s + t Q s \<le> t (\<lambda>s. P s + Q s) s"
    by(auto)
  also {
    from sP sQ have "sound (\<lambda>s. P s + Q s)" by(auto)
    with eq have "t (\<lambda>s. P s + Q s) s = u (\<lambda>s. P s + Q s) s"
      by(simp add:equiv_transD)
  }
  finally show "u P s + u Q s \<le> u (\<lambda>s. P s + Q s) s" .
qed

text {* Sublinearity and feasibility imply sub-additivity. *}
lemma sublinear_subadd:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes slt: "sublinear t"
      and ft:  "feasible t"
  shows "sub_add t"
proof
  fix P::"'s \<Rightarrow> real" and Q::"'s \<Rightarrow> real" and s::'s
  assume sP: "sound P" and sQ: "sound Q"

  with ft have "sound (t P)" "sound (t Q)" by(auto)
  hence "0 \<le> t P s" and "0 \<le> t Q s" by(auto)
  hence "0 \<le> t P s + t Q s" by(auto)
  hence "... = ...\<ominus> 0" by(simp)

  also from sP sQ
  have "... \<le> t (\<lambda>s. P s + Q s \<ominus> 0) s"
    by(rule sublinearD[OF slt, where a=1 and b=1 and c=0, simplified])

  also {
    from sP sQ have "\<And>s. 0 \<le> P s" and "\<And>s. 0 \<le> Q s" by(auto)
    hence "\<And>s. 0 \<le> P s + Q s" by(auto)
    hence "t (\<lambda>s. P s + Q s \<ominus> 0) s = t (\<lambda>s. P s + Q s) s"
      by(simp)
  }

  finally show "t P s + t Q s \<le> t (\<lambda>s. P s + Q s) s" .
qed

text {* A few properties following from sub-additivity: *}
lemma standard_negate:
  assumes ht: "healthy t"
      and sat: "sub_add t"
  shows "t \<guillemotleft>P\<guillemotright> s + t \<guillemotleft>\<N> P\<guillemotright> s \<le> 1"
proof -
  from sat have "t \<guillemotleft>P\<guillemotright> s + t \<guillemotleft>\<N> P\<guillemotright> s \<le> t (\<lambda>s. \<guillemotleft>P\<guillemotright> s + \<guillemotleft>\<N> P\<guillemotright> s) s" by(auto)
  also have "... = t (\<lambda>s. 1) s" by(simp add:negate_embed)
  also {
    from ht have "bounded_by 1 (t (\<lambda>s. 1))" by(auto)
    hence "t (\<lambda>s. 1) s \<le> 1" by(auto)
  }
  finally show ?thesis .
qed

lemma sub_add_setsum:
  fixes t::"'s trans" and S::"'a set"
  assumes sat: "sub_add t"
      and ht: "healthy t"
      and sP: "\<And>x. sound (P x)"
  shows "(\<lambda>x. \<Sum>y\<in>S. t (P y) x) \<le> t (\<lambda>x. \<Sum>y\<in>S. P y x)"
proof(cases "infinite S", simp_all add:ht)
  assume fS: "finite S"
  show ?thesis
  proof(rule finite_induct[OF fS le_funI le_funI], simp_all)
    fix s::'s
    from ht have "sound (t (\<lambda>s. 0))" by(auto)
    thus "0 \<le> t (\<lambda>s. 0) s" by(auto)

    fix F::"'a set" and x::'a
    assume IH: "\<lambda>a. \<Sum>y\<in>F. t (P y) a \<tturnstile> t (\<lambda>x. \<Sum>y\<in>F. P y x)"
    hence "t (P x) s + (\<Sum>y\<in>F. t (P y) s) \<le>
           t (P x) s + t (\<lambda>x. \<Sum>y\<in>F. P y x) s"
      by(auto intro:add_left_mono)
    also from sat sP
    have "... \<le> t (\<lambda>xa. P x xa + (\<Sum>y\<in>F. P y xa)) s"
      by(auto intro!:sub_addD[OF sat] setsum_sound)
    finally
    show "t (P x) s + (\<Sum>y\<in>F. t (P y) s) \<le>
          t (\<lambda>xa. P x xa + (\<Sum>y\<in>F. P y xa)) s" .
  qed
qed

lemma sub_add_guard_split:
  fixes t::"'s::finite trans" and P::"'s expect" and s::'s
  assumes sat: "sub_add t"
      and ht: "healthy t"
      and sP: "sound P"
  shows "(\<Sum>y\<in>{s. G s}.  P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> s) +
         (\<Sum>y\<in>{s. \<not>G s}. P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> s) \<le> t P s"
proof -
  have "{s. G s} \<inter> {s. \<not>G s} = {}" by(blast)
  hence "(\<Sum>y\<in>{s. G s}.  P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> s) +
         (\<Sum>y\<in>{s. \<not>G s}. P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> s) =
         (\<Sum>y\<in>({s. G s} \<union> {s. \<not>G s}). P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> s)"
    by(auto intro: setsum.union_disjoint[symmetric])
  also {
    have "{s. G s} \<union> {s. \<not>G s} = UNIV" by (blast)
    hence "(\<Sum>y\<in>({s. G s} \<union> {s. \<not>G s}). P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> s) =
           (\<lambda>x. \<Sum>y\<in>UNIV. P y * t (\<lambda>x. \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x) s"
      by(simp)
  }
  also {
    from sP have "\<And>y. 0 \<le> P y" by(auto)
    with healthy_scalingD[OF ht]
    have "(\<lambda>x. \<Sum>y\<in>UNIV. P y * t (\<lambda>x. \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x) s =
          (\<lambda>x. \<Sum>y\<in>UNIV. t (\<lambda>x. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x) s"
      by(simp add:scalingD)
  }
  also {
    from sat ht sP
    have "(\<lambda>x. \<Sum>y\<in>UNIV. t (\<lambda>x. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x) \<le>
          t (\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
      by(intro sub_add_setsum sound_intros, auto)
    hence "(\<lambda>x. \<Sum>y\<in>UNIV. t (\<lambda>x. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x) s \<le>
          t (\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) s" by(auto)
  }
  also {
    have rw1: "(\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) =
               (\<lambda>x. \<Sum>y\<in>UNIV. if y = x then P y else 0)"
      by(auto intro!:setsum.cong)
    also from sP have "... \<tturnstile> P"
      by(cases "finite (UNIV::'s set)", auto simp:setsum.delta)
    finally have leP: "\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft> \<lambda>z. z = y \<guillemotright> x \<tturnstile> P" .
    moreover have "sound (\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
    proof(intro soundI2 bounded_byI nnegI setsum_nonneg ballI)
      fix x
      from leP have "(\<Sum>y\<in>UNIV. P y * \<guillemotleft> \<lambda>z. z = y \<guillemotright> x) \<le> P x" by(auto)
      also from sP have "... \<le> bound_of P" by(auto)
      finally show "(\<Sum>y\<in>UNIV. P y * \<guillemotleft> \<lambda>z. z = y \<guillemotright> x) \<le> bound_of P" .
      fix y
      from sP show "0 \<le> P y * \<guillemotleft> \<lambda>z. z = y \<guillemotright> x"
        by(auto intro:mult_nonneg_nonneg)
    qed
    ultimately have "t (\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) s \<le> t P s"
      using sP by(auto intro:le_funD[OF mono_transD, OF healthy_monoD, OF ht])
  }
  finally show ?thesis .
qed

subsubsection {* Sub-distributivity *}

definition sub_distrib ::
  "(('s \<Rightarrow> real) \<Rightarrow> ('s \<Rightarrow> real)) \<Rightarrow> bool"
where
  "sub_distrib t \<longleftrightarrow> (\<forall>P s. sound P \<longrightarrow> t P s \<ominus> 1 \<le> t (\<lambda>s'. P s' \<ominus> 1) s)"

lemma sub_distribI[intro]:
  "\<lbrakk> \<And>P s. sound P \<Longrightarrow> t P s \<ominus> 1 \<le> t (\<lambda>s'. P s' \<ominus> 1) s \<rbrakk> \<Longrightarrow> sub_distrib t"
  by(simp add:sub_distrib_def)

lemma sub_distribI2:
  "\<lbrakk> \<And>P. sound P \<Longrightarrow> \<lambda>s. t P s \<ominus> 1 \<tturnstile> t (\<lambda>s. P s \<ominus> 1) \<rbrakk> \<Longrightarrow> sub_distrib t"
  by(auto)

lemma sub_distribD[dest]:
  "\<lbrakk> sub_distrib t; sound P \<rbrakk> \<Longrightarrow> t P s \<ominus> 1 \<le> t (\<lambda>s'. P s' \<ominus> 1) s"
  by(simp add:sub_distrib_def)

lemma equiv_sub_distrib:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes eq: "equiv_trans t u"
      and sd: "sub_distrib t"
  shows "sub_distrib u"
proof
  fix P::"'s \<Rightarrow> real" and s::"'s"
  assume sP: "sound P"

  with eq have "u P s \<ominus> 1 = t P s \<ominus> 1" by(simp add:equiv_transD)
  also from sP sd have "... \<le> t (\<lambda>s. P s \<ominus> 1) s" by(auto)
  also from sP eq have "... = u (\<lambda>s. P s \<ominus> 1) s"
    by(simp add:equiv_transD tminus_sound)
  finally show "u P s \<ominus> 1 \<le> u (\<lambda>s. P s \<ominus> 1) s" .
qed

text {* Sublinearity implies sub-distributivity: *}
lemma sublinear_sub_distrib:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes slt: "sublinear t"
  shows "sub_distrib t"
proof
  fix P::"'s \<Rightarrow> real" and s::'s
  assume sP: "sound P"
  moreover have "sound (\<lambda>_. 0)" by(auto)
  ultimately show "t P s \<ominus> 1 \<le> t (\<lambda>s. P s \<ominus> 1) s"
    by(rule sublinearD[OF slt, where a=1 and b=0 and c=1, simplified])
qed

text {* Healthiness, sub-additivity and sub-distributivity imply
  sublinearity.  This is how we usually show sublinearity. *}
lemma sd_sa_sublinear:
  fixes t::"('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes sdt: "sub_distrib t" and sat: "sub_add t" and ht: "healthy t"
  shows "sublinear t"
proof
  fix P::"'s \<Rightarrow> real" and Q::"'s \<Rightarrow> real" and s::'s
  and a::real and b::real and c::real
  assume sP: "sound P" and sQ: "sound Q"
     and nna: "0 \<le> a" and nnb: "0 \<le> b" and nnc: "0 \<le> c"

  from ht sP sQ nna nnb
  have saP: "sound (\<lambda>s. a * P s)" and staP: "sound (\<lambda>s. a * t P s)"
   and sbQ: "sound (\<lambda>s. b * Q s)" and stbQ: "sound (\<lambda>s. b * t Q s)"
    by(auto intro:sc_sound)
  hence sabPQ:  "sound (\<lambda>s. a * P s + b * Q s)"
    and stabPQ: "sound (\<lambda>s. a * t P s + b * t Q s)"
    by(auto intro:sound_sum)

  from ht sP sQ nna nnb
  have "a * t P s + b * t Q s = t (\<lambda>s. a * P s) s + t (\<lambda>s. b * Q s) s"
    by(simp add:scalingD healthy_scalingD)
  also from saP sbQ sat
  have "t (\<lambda>s. a * P s) s + t (\<lambda>s. b * Q s) s \<le>
        t (\<lambda>s. a * P s + b * Q s) s" by(blast)
  finally
  have notm: "a * t P s + b * t Q s \<le> t (\<lambda>s. a * P s + b * Q s) s" .

  show "a * t P s + b * t Q s \<ominus> c \<le> t (\<lambda>s'. a * P s' + b * Q s' \<ominus> c) s"
  proof(cases "c = 0")
    case True note z = this
    from stabPQ have "\<And>s. 0 \<le> a * t P s + b * t Q s" by(auto)
    moreover from sabPQ
    have "\<And>s. 0 \<le> a * P s + b * Q s" by(auto)
    ultimately show ?thesis by(simp add:z notm)
  next
    case False note nz = this
    from nz and nnc have nni: "0 \<le> inverse c" by(auto)

    have "\<And>s. (inverse c * a) * P s + (inverse c * b) * Q s =
              inverse c * (a * P s + b * Q s)"
      by(simp add: divide_simps)
    with sabPQ and nni
    have si: "sound (\<lambda>s. (inverse c * a) * P s + (inverse c * b) * Q s)"
      by(auto intro:sc_sound)
    hence sim: "sound (\<lambda>s. (inverse c * a) * P s + (inverse c * b) * Q s \<ominus> 1)"
      by(auto intro!:tminus_sound)

    from nz
    have "a * t P s + b * t Q s \<ominus> c =
          (c * inverse c) * a * t P s +
          (c * inverse c) * b * t Q s \<ominus> c"
      by(simp)
    also
    have "... = c * (inverse c * a * t P s) +
                c * (inverse c * b * t Q s) \<ominus> c"
      by(simp add:field_simps)
    also from nnc
    have "... = c * (inverse c * a * t P s + inverse c * b * t Q s \<ominus> 1)"
      by(simp add:distrib_left tminus_left_distrib)
    also {
      have X: "\<And>s. (inverse c * a) * t P s + (inverse c * b) * t Q s =
                   inverse c * (a * t P s + b * t Q s)" by(simp add: divide_simps)
      also from nni and notm
      have "inverse c * (a * t P s + b * t Q s) \<le>
            inverse c * (t (\<lambda>s. a * P s + b * Q s) s)"
        by(blast intro:mult_left_mono)
      also from nni ht sabPQ
      have "... = t (\<lambda>s. (inverse c * a) * P s + (inverse c * b) * Q s) s"
        by(simp add:scalingD[OF healthy_scalingD, OF ht] algebra_simps)
      finally
      have "(inverse c * a) * t P s + (inverse c * b) * t Q s \<ominus> 1 \<le>
            t (\<lambda>s. (inverse c * a) * P s + (inverse c * b) * Q s) s \<ominus> 1"
        by(rule tminus_left_mono)
      also {
        from sdt si
        have "t (\<lambda>s. (inverse c * a) * P s + (inverse c * b) * Q s) s \<ominus> 1 \<le>
              t (\<lambda>s. (inverse c * a) * P s + (inverse c * b) * Q s \<ominus> 1) s"
          by(blast)
      }
      finally
      have "c * (inverse c * a * t P s + inverse c * b * t Q s \<ominus> 1) \<le>
            c * t (\<lambda>s. inverse c * a * P s + inverse c * b * Q s \<ominus> 1) s"
        using nnc by(blast intro:mult_left_mono)
    }
    also from nnc ht sim
    have "c * t (\<lambda>s. inverse c * a * P s + inverse c * b * Q s \<ominus> 1) s
          = t (\<lambda>s. c * (inverse c * a * P s + inverse c * b * Q s \<ominus> 1)) s"
      by(simp add:scalingD healthy_scalingD)
    also from nnc
    have "... = t (\<lambda>s. c * (inverse c * a * P s) +
                       c * (inverse c * b * Q s) \<ominus> c) s"
      by(simp add:distrib_left tminus_left_distrib)
    also have "... = t (\<lambda>s. (c * inverse c) * a * P s +
                            (c * inverse c) * b * Q s \<ominus> c) s"
      by(simp add:field_simps)
    finally
    show "a * t P s + b * t Q s \<ominus> c \<le> t (\<lambda>s'. a * P s' + b * Q s' \<ominus> c) s"
      using nz by(simp)
  qed
qed

subsubsection {* Sub-conjunctivity *}
definition
  sub_conj :: "(('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real) \<Rightarrow> bool"
where
  "sub_conj t \<equiv> \<forall>P Q. (sound P \<and> sound Q) \<longrightarrow>
                       t P && t Q \<tturnstile> t (P && Q)"

lemma sub_conjI[intro]:
  "\<lbrakk> \<And>P Q. \<lbrakk> sound P; sound Q \<rbrakk> \<Longrightarrow>
           t P && t Q \<tturnstile> t (P && Q) \<rbrakk> \<Longrightarrow> sub_conj t"
  unfolding sub_conj_def by(simp)

lemma sub_conjD[dest]:
  "\<lbrakk> sub_conj t; sound P; sound Q \<rbrakk> \<Longrightarrow> t P && t Q \<tturnstile> t (P && Q)"
  unfolding sub_conj_def by(simp)

lemma sub_conj_wp_twice:
  fixes f::"'s \<Rightarrow> (('s \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real)"
  assumes all: "\<forall>s. sub_conj (f s)"
  shows "sub_conj (\<lambda>P s. f s P s)"
proof(rule sub_conjI, rule le_funI)
  fix P::"'s \<Rightarrow> real" and Q::"'s \<Rightarrow> real" and s
  assume sP: "sound P" and sQ: "sound Q"

  have "((\<lambda>s. f s P s) && (\<lambda>s. f s Q s)) s = (f s P && f s Q) s"
    by(simp add:exp_conj_def)
  also {
    from all have "sub_conj (f s)" by(blast)
    with sP and sQ have "(f s P && f s Q) s \<le> f s (P && Q) s"
      by(blast)
  }
  finally show "((\<lambda>s. f s P s) && (\<lambda>s. f s Q s)) s \<le> f s (P && Q) s" .
qed

text {* Sublinearity implies sub-conjunctivity: *}

subsubsection {* Sublinearity under equivalence *}

text {* Sublinearity is preserved by equivalence. *}
lemma equiv_sublinear:
  "\<lbrakk> equiv_trans t u; sublinear t; healthy t \<rbrakk> \<Longrightarrow> sublinear u"
  by(iprover intro:sd_sa_sublinear healthy_equivI
             dest:equiv_sub_distrib equiv_sub_add
                  sublinear_sub_distrib sublinear_subadd
                  healthy_feasibleD)

subsection {* Determinism *}

text {* Transformers which are both additive, and maximal among those that
satisfy feasibility are \emph{deterministic}, and will turn out to be maximal
in the refinement order. *}

subsubsection {* Additivity *}
text {* Full additivity is not generally satisfied.  It holds for
  (sub-)probabilistic transformers however. *}
definition
  additive :: "(('a \<Rightarrow> real) \<Rightarrow> 'a \<Rightarrow> real) \<Rightarrow> bool"
where
  "additive t \<equiv> \<forall>P Q. (sound P \<and> sound Q) \<longrightarrow>
                      t (\<lambda>s. P s + Q s) = (\<lambda>s. t P s + t Q s)"

lemma additiveD:
  "\<lbrakk> additive t; sound P; sound Q \<rbrakk> \<Longrightarrow> t (\<lambda>s. P s + Q s) = (\<lambda>s. t P s + t Q s)"
  by(simp add:additive_def)

lemma additiveI[intro]:
  "\<lbrakk> \<And>P Q s. \<lbrakk> sound P; sound Q \<rbrakk> \<Longrightarrow> t (\<lambda>s. P s + Q s) s = t P s + t Q s \<rbrakk> \<Longrightarrow>
   additive t"
  unfolding additive_def by(blast)

text {* Additivity is strictly stronger than sub-additivity. *}
lemma additive_sub_add:
  "additive t \<Longrightarrow> sub_add t"
  by(simp add:sub_addI additiveD)

text {* The additivity property extends to finite summation. *}
lemma additive_setsum:
  fixes S::"'s set"
  assumes additive: "additive t"
      and healthy:  "healthy t"
      and finite:   "finite S"
      and sPz:      "\<And>z. sound (P z)"
  shows "t (\<lambda>x. \<Sum>y\<in>S. P y x) = (\<lambda>x. \<Sum>y\<in>S. t (P y) x)"
proof(rule finite_induct, simp_all add:assms)
  fix z::'s and T::"'s set"
  assume finT: "finite T"
     and IH: "t (\<lambda>x. \<Sum>y\<in>T. P y x) = (\<lambda>x. \<Sum>y\<in>T. t (P y) x)"

  from additive sPz
  have "t (\<lambda>x. P z x + (\<Sum>y\<in>T. P y x)) =
        (\<lambda>x. t (P z) x +  t (\<lambda>x. \<Sum>y\<in>T. P y x) x)"
    by(auto intro!:setsum_sound additiveD)
  also from IH
  have "... = (\<lambda>x. t (P z) x + (\<Sum>y\<in>T. t (P y) x))"
    by(simp)
  finally show "t (\<lambda>x. P z x + (\<Sum>y\<in>T. P y x)) =
                (\<lambda>x. t (P z) x + (\<Sum>y\<in>T. t (P y) x))" .
qed

text {* An additive transformer (over a finite state space) is linear: it is
  simply the weighted sum of final expectation values, the weights being the
  probability of reaching a given final state.  This is useful for reasoning
  using the forward, or ``gambling game'' interpretation. *}
lemma additive_delta_split:
  fixes t::"('s::finite \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes additive: "additive t"
      and ht: "healthy t"
      and sP: "sound P"
  shows "t P x = (\<Sum>y\<in>UNIV. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
proof -
  have "\<And>x. (\<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) =
            (\<Sum>y\<in>UNIV. if y = x then P y else 0)"
    by(auto intro:setsum.cong)
  also have "\<And>x. ... x = P x"
    by(simp add:setsum.delta)
  finally
  have "t P x = t (\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x"
    by(simp)
  also {
    from sP have "\<And>z. sound (\<lambda>a. P z * \<guillemotleft> \<lambda>za. za = z \<guillemotright> a)"
      by(auto intro!:mult_sound)
    hence "t (\<lambda>x. \<Sum>y\<in>UNIV. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x =
           (\<Sum>y\<in>UNIV. t (\<lambda>x. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x)"
      by(subst additive_setsum, simp_all add:assms)
  }
  also from sP
  have "(\<Sum>y\<in>UNIV. t (\<lambda>x. P y * \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) x) =
        (\<Sum>y\<in>UNIV. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
    by(subst scalingD[OF healthy_scalingD, OF ht], auto)
  finally show "t P x = (\<Sum>y\<in>UNIV. P y * t \<guillemotleft> \<lambda>z. z = y \<guillemotright> x)" .
qed

text {* We can group the states in the linear form, to split on the value
  of a predicate (guard). *}
lemma additive_guard_split:
  fixes t::"('s::finite \<Rightarrow> real) \<Rightarrow> 's \<Rightarrow> real"
  assumes additive: "additive t"
      and ht: "healthy t"
      and sP: "sound P"
  shows "t P x = (\<Sum>y\<in>{s.   G s}. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) +
                 (\<Sum>y\<in>{s. \<not> G s}. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
proof -
  from assms
  have "t P x = (\<Sum>y\<in>UNIV. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
    by(rule additive_delta_split)
  also {
    have "UNIV = {s. G s} \<union> {s. \<not> G s}"
      by(auto)
    hence "(\<Sum>y\<in>UNIV. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) =
           (\<Sum>y\<in>{s. G s} \<union> {s. \<not> G s}. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
      by(simp)
  }
  also
  have "(\<Sum>y\<in>{s. G s} \<union> {s. \<not> G s}. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) =
        (\<Sum>y\<in>{s.   G s}. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x) +
        (\<Sum>y\<in>{s. \<not> G s}. P y * t \<guillemotleft>\<lambda>z. z = y\<guillemotright> x)"
    by(auto intro:setsum.union_disjoint)
  finally show ?thesis .
qed

subsubsection {* Maximality *}
definition
  maximal :: "(('a \<Rightarrow> real) \<Rightarrow> 'a \<Rightarrow> real) \<Rightarrow> bool"
where
  "maximal t \<equiv> \<forall>c. 0 \<le> c \<longrightarrow> t (\<lambda>_. c) = (\<lambda>_. c)"

lemma maximalI[intro]:
  "\<lbrakk> \<And>c. 0 \<le> c \<Longrightarrow> t (\<lambda>_. c) = (\<lambda>_. c) \<rbrakk> \<Longrightarrow> maximal t"
  by(simp add:maximal_def)

lemma maximalD[dest]:
  "\<lbrakk> maximal t; 0 \<le> c \<rbrakk>  \<Longrightarrow> t (\<lambda>_. c) = (\<lambda>_. c)"
  by(simp add:maximal_def)

text {* A transformer that is both additive and maximal is deterministic: *}
definition determ :: "(('a \<Rightarrow> real) \<Rightarrow> 'a \<Rightarrow> real) \<Rightarrow> bool"
where
  "determ t \<equiv> additive t \<and> maximal t"

lemma determI[intro]:
  "\<lbrakk> additive t; maximal t \<rbrakk> \<Longrightarrow> determ t"
  by(simp add:determ_def)

lemma determ_additiveD[intro]:
  "determ t \<Longrightarrow> additive t"
  by(simp add:determ_def)

lemma determ_maximalD[intro]:
  "determ t \<Longrightarrow> maximal t"
  by(simp add:determ_def)

text {* For a fully-deterministic transformer, a transformed standard
  expectation, and its transformed negation are complementary. *}
lemma determ_negate:
  assumes determ:  "determ t"
  shows "t \<guillemotleft>P\<guillemotright> s + t \<guillemotleft>\<N> P\<guillemotright> s = 1"
proof -
  have "t \<guillemotleft>P\<guillemotright> s + t \<guillemotleft>\<N> P\<guillemotright> s = t (\<lambda>s. \<guillemotleft>P\<guillemotright> s + \<guillemotleft>\<N> P\<guillemotright> s) s"
    by(simp add:additiveD determ determ_additiveD)
  also {
    have "\<And>s. \<guillemotleft>P\<guillemotright> s + \<guillemotleft>\<N> P\<guillemotright> s = 1"
      by(case_tac "P s", simp_all)
    hence "t (\<lambda>s. \<guillemotleft>P\<guillemotright> s + \<guillemotleft>\<N> P\<guillemotright> s) = t (\<lambda>s. 1)"
      by(simp)
  }
  also have "t (\<lambda>s. 1) = (\<lambda>s. 1)"
    by(simp add:maximalD determ determ_maximalD)
  finally show ?thesis .
qed

subsection {* Modular Reasoning *}

text {* The emphasis of a mechanised logic is on automation, and letting
  the computer tackle the large, uninteresting problems.  However, as
  terms generally grow exponentially in the size of a program, it is
  still essential to break up a proof and reason in a modular fashion.

  The following rules allow proof decomposition, and later will be
  incorporated into a verification condition generator. *}

lemma entails_combine:
  assumes wp1: "P \<tturnstile> t R"
      and wp2: "Q \<tturnstile> t S"
      and sc:  "sub_conj t"
      and sR:  "sound R"
      and sS:  "sound S"
  shows "P && Q \<tturnstile> t (R && S)"
proof -
  from wp1 and wp2 have "P && Q \<tturnstile> t R && t S"
    by(blast intro:entails_frame)
  also from sc and sR and sS have "... \<le> t (R && S)"
    by(rule sub_conjD)
  finally show ?thesis .
qed

text {* These allow mismatched results to be composed *}

lemma entails_strengthen_post:
  "\<lbrakk> P \<tturnstile> t Q; healthy t; sound R; Q \<tturnstile> R; sound Q \<rbrakk> \<Longrightarrow> P \<tturnstile> t R"
  by(blast intro:entails_trans)

lemma entails_weaken_pre:
  "\<lbrakk> Q \<tturnstile> t R; P \<tturnstile> Q \<rbrakk> \<Longrightarrow> P \<tturnstile> t R"
  by(blast intro:entails_trans)

text {* This rule is unique to pGCL.  Use it to scale the post-expectation
        of a rule to 'fit under' the precondition you need to satisfy. *}
lemma entails_scale:
  assumes wp: "P \<tturnstile> t Q" and h: "healthy t"
      and sQ: "sound Q" and pos: "0 \<le> c"
  shows "(\<lambda>s. c * P s) \<tturnstile> t (\<lambda>s. c * Q s)"
proof(rule le_funI)
  fix s
  from pos and wp have "c * P s \<le> c * t Q s"
    by(auto intro:mult_left_mono)
  with sQ pos h show "c * P s \<le> t (\<lambda>s. c * Q s) s"
    by(simp add:scalingD healthy_scalingD)
qed

subsection {* Transforming Standard Expectations *}

text {* Reasoning with \emph{standard} expectations, those obtained
  by embedding a predicate, is often easier, as the analogues of
  many familiar boolean rules hold in modified form. *}

text {* One may use a standard pre-expectation as an assumption: *}
lemma use_premise:
  assumes h: "healthy t" and wP: "\<And>s. P s \<Longrightarrow> 1 \<le> t \<guillemotleft>Q\<guillemotright> s"
  shows "\<guillemotleft>P\<guillemotright> \<tturnstile> t \<guillemotleft>Q\<guillemotright>"
proof(rule entailsI)
  fix s show "\<guillemotleft>P\<guillemotright> s \<le> t \<guillemotleft>Q\<guillemotright> s"
  proof(cases "P s")
    case True with wP show ?thesis by(auto)
  next
    case False with h show ?thesis by(auto)
  qed
qed

text {* The other direction works too. *}
lemma fold_premise:
  assumes ht: "healthy t"
  and wp: "\<guillemotleft>P\<guillemotright> \<tturnstile> t \<guillemotleft>Q\<guillemotright>"
  shows "\<forall>s. P s \<longrightarrow> 1 \<le> t \<guillemotleft>Q\<guillemotright> s"
proof(clarify)
  fix s assume "P s"
  hence "1 = \<guillemotleft>P\<guillemotright> s" by(simp)
  also from wp have "... \<le> t \<guillemotleft>Q\<guillemotright> s" by(auto)
  finally show "1 \<le> t \<guillemotleft>Q\<guillemotright> s" .
qed

text {* Predicate conjunction behaves as expected: *}
lemma conj_post:
  "\<lbrakk> P \<tturnstile> t \<guillemotleft>\<lambda>s. Q s \<and> R s\<guillemotright>; healthy t \<rbrakk> \<Longrightarrow> P \<tturnstile> t \<guillemotleft>Q\<guillemotright>"
  by(blast intro:entails_strengthen_post implies_entails)

text {* Similar to @{thm use_premise}, but more general. *}
lemma entails_pconj_assumption:
  assumes f: "feasible t" and wP: "\<And>s. P s \<Longrightarrow> Q s \<le> t R s"
      and uQ: "unitary Q" and uR: "unitary R"
  shows "\<guillemotleft>P\<guillemotright> && Q \<tturnstile> t R"
  unfolding exp_conj_def
proof(rule entailsI)
  fix s show "\<guillemotleft>P\<guillemotright> s .& Q s \<le> t R s"
  proof(cases "P s")
    case True
    moreover from uQ have "0 \<le> Q s" by(auto)
    ultimately show ?thesis by(simp add:pconj_lone wP)
  next
    case False
    moreover from uQ have "Q s \<le> 1" by(auto)
    ultimately show ?thesis using assms by(simp, blast)
  qed
qed

end