Theory PhiSem_CF_Basic

chapter ‹Basic Control Flow›

theory PhiSem_CF_Basic
  imports PhiSem_Generic_Boolean "HOL-Library.While_Combinator"
begin

section ‹Instructions›

subsection ‹Non-Branching Selection›

definition op_sel :: "TY  (VAL × VAL × VAL, VAL) proc'"
  where "op_sel TY =
    φM_caseV (λvc. φM_caseV (λva vb.
    φM_getV 𝖻𝗈𝗈𝗅 sem_dest_bool vc (λc.
    φM_getV TY id va (λa.
    φM_getV TY id vb (λb.
    Return (φarg (if c then a else b)))))))"

subsection ‹Branch›

definition op_if :: "'ret proc
                   'ret proc
                   (VAL,'ret) proc'"
  where "op_if brT brF v =
    φM_getV 𝖻𝗈𝗈𝗅 sem_dest_bool v (λc. (if c then brT else brF))"

subsection ‹While Loop›

inductive SemDoWhile :: "VAL proc  resource  unit comp  bool" where
  "Success (φarg (sem_mk_bool False)) res  f s  SemDoWhile f s (Success (φarg ()) res)"
| "Success (φarg (sem_mk_bool True)) res  f s  SemDoWhile f res s''  SemDoWhile f s s''"
| "Abnormal v e  f s  SemDoWhile f s (Abnormal v e)"
| "NonTerm  f s  SemDoWhile f s NonTerm"
| "AssumptionBroken  f s  SemDoWhile f s AssumptionBroken"
| "Invalid  f s  SemDoWhile f s Invalid"

lemma " y. SemDoWhile ((λres. Return (φarg (sem_mk_bool True)) res) :: VAL proc) res y"
  apply rule apply (elim exE) subgoal for y
    apply (induct "((λres. Return (φarg (sem_mk_bool True)) (res::resource)) :: VAL proc)" res y
           rule: SemDoWhile.induct)
       apply (simp_all add: Return_def det_lift_def) . .

definition op_do_while :: " VAL proc  unit proc"
  where "op_do_while f s = Collect (SemDoWhile f s)"


subsection ‹Recursion›

inductive SemRec :: "(('a,'b) proc'  ('a,'b) proc')
             'a φarg  resource  'b comp set  bool"
where
  SemRec_I0: "(g. F g x res = y)  SemRec F x res y"
| SemRec_IS: "SemRec (F o F) x res y  SemRec F x res y"

definition op_fix_point :: "(('a,'b) proc'  ('a,'b) proc')
                          ('a,'b) proc'"
  where "op_fix_point F x s = (if (t. SemRec F x s t) then The (SemRec F x s) else {})"

ML Synchronized.change Phi_Syntax.semantic_oprs (Symtab.update (const_nameop_fix_point, 0))


subsubsection ‹Simple Properties›

lemma SemRec_IR: "SemRec F x r y  SemRec (F o F) x r y"
  by (induct rule: SemRec.induct, rule SemRec_I0, simp)

lemma SemRec_deterministic:
  assumes "SemRec c s r s1" and "SemRec c s r s2" shows "s1 = s2"
proof -
  have "SemRec c s r s1  (s2. SemRec c s r s2  s1 = s2)"
    apply (induct rule: SemRec.induct)
     apply clarify
    subgoal for F a b y s2 apply (rotate_tac 1)
      apply (induct rule: SemRec.induct) by auto
    apply clarify apply (blast intro: SemRec_IR) done
  thus ?thesis using assms by simp
qed

lemma SemRec_deterministic2: " SemRec body s r x  The (SemRec body s r) = x"
  using SemRec_deterministic by (metis theI_unique)



section ‹Abstraction of Procedures›

subsubsection ‹Syntax for Annotations›

consts Invariant :: bool  bool ("Inv: _" [100] 36)
consts Guard :: bool  bool ("Guard: _" [100] 36)
consts End   :: bool  bool ("End: _" [100] 36)
consts Transition :: 'a  bool ("Transition: _" [100] 36)

subsection ‹Branch-like›

lemma sel_φapp:
  Semantic_Type' (a  A) TY
 Semantic_Type' (b  B) TY
 𝗉𝗋𝗈𝖼 op_sel TY (φV_pair rawc (φV_pair rawa rawb)) 
        c  𝗏𝖺𝗅[rawc] 𝔹 a  𝗏𝖺𝗅[rawa] A b  𝗏𝖺𝗅[rawb] B
     (if c then a else b)  𝗏𝖺𝗅 (if c then A else B)
    
  unfolding op_sel_def
  by ((cases rawc; cases rawb; cases rawa; cases c; simp add: Semantic_Type'_def subset_iff),
      rule, rule, rule, simp add: φexpns WT_bool, blast, rule, simp add: φexpns WT_bool, rule,
      simp add: φexpns WT_bool, rule, simp add: φexpns WT_bool, rule, rule, rule,
      simp add: φexpns WT_bool, blast, rule, simp add: φexpns WT_bool, rule, simp add: φexpns WT_bool,
      rule, simp add: φexpns WT_bool)

lemma branch_φapp:
  (𝗉𝗋𝖾𝗆𝗂𝗌𝖾   C  𝗉𝗋𝗈𝖼 brT  X  YT  𝗍𝗁𝗋𝗈𝗐𝗌 ET )
 (𝗉𝗋𝖾𝗆𝗂𝗌𝖾 ¬ C  𝗉𝗋𝗈𝖼 brF  X  YF  𝗍𝗁𝗋𝗈𝗐𝗌 EF )
 (v. If C (YT v) (YF v) 𝗍𝗋𝖺𝗇𝗌𝖿𝗈𝗋𝗆𝗌 Y v @tag invoke_br_join)
 𝗉𝗋𝗈𝖼 op_if brT brF rawc  C  𝗏𝖺𝗅[rawc] 𝔹 X  Y  𝗍𝗁𝗋𝗈𝗐𝗌 (λe. (ET e 𝗌𝗎𝖻𝗃 C) + (EF e 𝗌𝗎𝖻𝗃 ¬ C))
  unfolding op_if_def Premise_def Action_Tag_def
  by (cases rawc; cases C; simp; rule; simp add: φexpns WT_bool;
      insert φCONSEQ view_shift_by_implication view_shift_refl; blast)

proc "if":
  requires C: 𝗉𝗋𝗈𝖼 cond  X  𝗏𝖺𝗅 C  𝔹 X1  𝗍𝗁𝗋𝗈𝗐𝗌 E
      and brT: 𝗉𝗋𝖾𝗆𝗂𝗌𝖾   C  𝗉𝗋𝗈𝖼 brT  X1  YT  𝗍𝗁𝗋𝗈𝗐𝗌 ET
      and brF: 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 ¬ C  𝗉𝗋𝗈𝖼 brF  X1  YF  𝗍𝗁𝗋𝗈𝗐𝗌 EF
      and BC: (v. If C (YT v) (YF v) 𝗍𝗋𝖺𝗇𝗌𝖿𝗈𝗋𝗆𝗌 Y v @tag invoke_br_join)
  input  X
  output Y
  throws E + ET + EF
   C branch brT brF BC  .

ML Synchronized.change Phi_Syntax.semantic_oprs (Symtab.update (const_nameif, 3))



subsection ‹Loops›
 
lemma "__DoWhile__rule_φapp":
  " 𝗉𝗋𝗈𝖼 body  X x 𝗌𝗎𝖻𝗃 x. P x  (∃*x'. 𝗏𝖺𝗅 P x'  𝔹 X x')  𝗍𝗁𝗋𝗈𝗐𝗌 E
 𝗉𝗋𝗈𝖼 op_do_while body  X x 𝗌𝗎𝖻𝗃 x. P x  X x' 𝗌𝗎𝖻𝗃 x'. ¬ P x'  𝗍𝗁𝗋𝗈𝗐𝗌 E "
  unfolding op_do_while_def φProcedure_def
  apply (simp add: less_eq_BI_iff LooseState_expn')
  apply (rule allI impI conjI)+
  subgoal for comp R s
  apply (rotate_tac 2)
    apply (induct body comp s rule: SemDoWhile.induct;
           clarsimp simp add: times_list_def INTERP_SPEC)
    apply fastforce
    subgoal premises prems for res f s s'' c u v proof -
      have t1: uu. (x. (u v. uu = u * v  u  X x  v  R  u ## v)  P x)  s  INTERP_RES uu
        using prems(5) prems(6) prems(7) prems(8) prems(9) by blast
      show ?thesis
        by (insert _ _. (_. _)  _[THEN spec[where x=s], THEN spec[where x=R], THEN mp, OF t1]
                   prems(1) prems(3), fastforce)
    qed
    apply fastforce
    by blast .
  
proc (nodef) do_while:
  requires 𝗉𝖺𝗋𝖺𝗆 ( X' x 𝗌𝗎𝖻𝗃 x. Inv: invariant x  Guard: cond x)
       and V: 𝗋CALL X 𝗍𝗋𝖺𝗇𝗌𝖿𝗈𝗋𝗆𝗌 ( X' x 𝗌𝗎𝖻𝗃 x. invariant x  cond x) 𝗐𝗂𝗍𝗁 Any
       and B: x. 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 cond x  𝗉𝗋𝖾𝗆𝗂𝗌𝖾 invariant x
       𝗉𝗋𝗈𝖼 body  X' x  (𝗏𝖺𝗅 cond x'  𝔹 X' x' 𝗌𝗎𝖻𝗃 x'. invariant x')  𝗍𝗁𝗋𝗈𝗐𝗌 E
  input  X
  output X' x' 𝗌𝗎𝖻𝗃 x'. invariant x'  ¬ cond x'
  throws E
  
    apply_rule V[unfolded Action_Tag_def]
    apply_rule "__DoWhile__rule_φapp"[where P=cond and X=λx'. X' x' 𝗌𝗎𝖻𝗃 invariant x', simplified]
     B  !!
   .

ML Synchronized.change Phi_Syntax.semantic_oprs (Symtab.update (const_nameop_do_while, 2))

(*
We fail to infer the abstraction of the loop guard automatically but
require users to give by an annotation.
The main difficulty is about the nondeterminancy in higher-order unification.
In term‹cond x' ⦂ 𝔹› in the above rule, both ‹cond› and ‹x'› are schematic variables,
which means we cannot determine either of them via unification.
Even though the abstract state ‹x'› may be determined possibly in the unification of ‹X x'›,
to infer ‹cond x'› it is still a problem especially when ‹x'› is not a variable but a compounded
term and its expression may be shattered in and mixed up with the expression of ‹cond› after
simplifications like beta reduction,
causing it is very difficult to recover the actual abstract guard
‹cond› from the reduced composition ‹cond x'›.
*)

proc while:
  requires 𝗉𝖺𝗋𝖺𝗆 ( X x 𝗌𝗎𝖻𝗃 x. Inv: invariant x  Guard: cond x)
    and V: "X' 𝗍𝗋𝖺𝗇𝗌𝖿𝗈𝗋𝗆𝗌 ((X x 𝗋𝖾𝗆𝖺𝗂𝗇𝗌 R) 𝗌𝗎𝖻𝗃 x. invariant x) 𝗐𝗂𝗍𝗁 Any"
    and C: "x. 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 invariant x  𝗉𝗋𝗈𝖼 Cond  X x R  𝗏𝖺𝗅 cond x'  𝔹 X x' R 𝗌𝗎𝖻𝗃 x'. invariant x'  𝗍𝗁𝗋𝗈𝗐𝗌 E1"
    and B: "x. 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 invariant x  𝗉𝗋𝖾𝗆𝗂𝗌𝖾 cond x  𝗉𝗋𝗈𝖼 Body  X x R  X x' R 𝗌𝗎𝖻𝗃 x'. invariant x'  𝗍𝗁𝗋𝗈𝗐𝗌 E2"
  input  X'
  output X x R 𝗌𝗎𝖻𝗃 x. invariant x  ¬ cond x
  throws E1 + E2
   V C
    branch 
      do_while X vars R 𝗌𝗎𝖻𝗃 vars. Inv: invariant vars  Guard: cond vars
       B C 
    
      for R X vars 𝗌𝗎𝖻𝗃 vars. invariant vars  ¬ cond vars ;;
   .

ML Synchronized.change Phi_Syntax.semantic_oprs (Symtab.update (const_namewhile, 3))

proc (nodef) refine_while
  [unfolded φType_def[where T=X::'a  (FIC_N  FIC) BI]]:
  requires 𝗉𝖺𝗋𝖺𝗆 (X x 𝗌𝗎𝖻𝗃 x. Inv: invariant x  Guard: cond x  Transition: f x)
    and V: "X' 𝗍𝗋𝖺𝗇𝗌𝖿𝗈𝗋𝗆𝗌 (X x 𝗋𝖾𝗆𝖺𝗂𝗇𝗌 R) 𝗌𝗎𝖻𝗃 invariant x 𝗐𝗂𝗍𝗁 Any"
    and C: "x. 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 invariant x  𝗉𝗋𝗈𝖼 Cond  R X x  R X x 𝗏𝖺𝗅 cond x  𝔹  𝗍𝗁𝗋𝗈𝗐𝗌 E1"
    and B: "x. 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 invariant x  𝗉𝗋𝖾𝗆𝗂𝗌𝖾 cond x  𝗉𝗋𝗈𝖼 Body  R X x  R X x' 𝗌𝗎𝖻𝗃 x'. x' = f x  invariant x'  𝗍𝗁𝗋𝗈𝗐𝗌 E2"
  input X'
  output R X x' 𝗌𝗎𝖻𝗃 x'. x' = While_Combinator.while cond f x  invariant x'
  throws E1 + E2
  apply (represent_BI_pred_in_φType X)
   V
    while x'  X 𝗌𝗎𝖻𝗃 x' i.
        Inv: (x' = (f ^^ i) x  (k < i. cond ((f ^^ k) x))  (k  i. invariant ((f ^^ k) x)) ) 
        Guard: cond x'
     C 
     B  certified by (clarsimp, rule exI[where x=i+1],
                        auto simp add: less_Suc_eq_le φ,
                        (insert le_eq_less_or_eq the_φ(5) the_φ(7), fastforce)[1],
                        metis funpow.simps(2) le_SucE o_apply the_φ(6) the_φlemmata(3) the_φlemmata(4)) 

    have [φreason add]:
        y. 𝗉𝗋𝖾𝗆𝗂𝗌𝖾 (f ^^ y) x = While_Combinator.while cond f x
       X ((f ^^ y) x) 𝗍𝗋𝖺𝗇𝗌𝖿𝗈𝗋𝗆𝗌 X (While_Combinator.while cond f x)
      by (simp add: Premise_def)

   certified
    by (auto simp add: While_Combinator.while_def while_option_def φ; auto_sledgehammer) .


subsection ‹Recursion›

lemma "__op_recursion_simp__":
  "(g x' v'. (x'' v''. 𝗉𝗋𝗈𝖼 g v''   X x'' v''  λret. Y x'' ret  𝗍𝗁𝗋𝗈𝗐𝗌 E x'')
                       𝗉𝗋𝗈𝖼 F g v'  X x' v'    λret. Y x'  ret  𝗍𝗁𝗋𝗈𝗐𝗌 E x' )
 x v. 𝗉𝗋𝗈𝖼 op_fix_point F v  X x v  λret. Y x ret  𝗍𝗁𝗋𝗈𝗐𝗌 E x"
  unfolding op_fix_point_def φProcedure_def atomize_all
  apply (clarsimp simp add: SemRec_deterministic2 less_eq_BI_iff del: subsetI)

  subgoal for x v comp a R w
    apply (rotate_tac 1) apply (induct rule: SemRec.induct)

    subgoal premises prems for F v res y
      using prems(4)[of λ_ _. {AssumptionBroken} x v, simplified, THEN spec[where x=res],
                     THEN spec[where x=R], THEN mp, OF prems(2), unfolded prems(1)] prems(3) by blast
    by (smt (z3) comp_apply) .

text ‹Instead, we use a variant of the above rule which in addition annotates the names
  of the values.›

lemma "__op_recursion__":
  "(g x' (v':: 'a φarg <named> 'names).
          P x'
       PROP Labelled label (Technical
          (x'' (v''::'a φarg <named> 'names).
              P x'' 
              𝗉𝗋𝗈𝖼 g (case_named id v'')  case_named (X x'') v''  λret. Y x'' ret  𝗍𝗁𝗋𝗈𝗐𝗌 E x''))
       𝗉𝗋𝗈𝖼 F g (case_named id v')  case_named (X x') v'    λret. Y x'  ret  𝗍𝗁𝗋𝗈𝗐𝗌 E x' )
 PROP Pure.prop (
      P x 
      𝗉𝗋𝗈𝖼 op_fix_point F v  X x v  λret. Y x ret  𝗍𝗁𝗋𝗈𝗐𝗌 E x
)"
  unfolding op_fix_point_def φProcedure_def atomize_all φarg_forall φarg_All Technical_def
            Pure.prop_def
  apply (clarsimp simp add: SemRec_deterministic2 less_eq_BI_iff del: subsetI)

  subgoal for comp a R w
    apply (rotate_tac 2) apply (induct rule: SemRec.induct)

    subgoal premises prems for F v res y
      using prems(4)[OF prems(5),
                     of λ_ _. {AssumptionBroken} v, simplified, THEN spec[where x=res],
                     THEN spec[where x=R], THEN mp, OF prems(2), unfolded prems(1)]
            prems(3) by blast
    by (smt (z3) comp_apply) .

ML_file ‹library/basic_recursion.ML›

attribute_setup recursive = Scan.repeat (Scan.lift Parse.term) >> (fn vars =>
    Phi_Modifier.wrap_to_attribute (fn (ctxt,sequent) =>
      case Phi_Toplevel.name_of_the_building_procedure ctxt
        of NONE => error "Name binding of the recursive procedure is mandatory."
         | SOME b => (
            let
             in if Binding.is_empty b
                then error "A recursive procedure cannot be anonymous."
                else if null vars then tracing "You may want to use syntax ‹recursive vars› to indicate \
                     \variables varying throught recursive calls." else ();
             PhiSem_Control_Flow.basic_recursive_mod Syntax.read_terms b vars (ctxt,sequent)
            end
           )
  ))


subsection ‹Syntax›

syntax "_while_" :: do_binds  do_binds  do_binds
                 ("((2𝗐𝗁𝗂𝗅𝖾 {//(_))//(2} {//(_))//})" [11,11] 20)
       "_if_" :: do_binds  do_binds  do_binds  do_binds
                 ("((2𝗂𝖿 {//(_))//(2} 𝗍𝗁𝖾𝗇 {//(_))//(2} 𝖾𝗅𝗌𝖾 {//(_))//})" [11,11,11] 20)
       "_fix_point_" :: idt  idt  do_binds  do_bind ("((2𝖿𝗂𝗑 _ '(_') {//)(_)//})" [100,100,10] 20)

optional_translations (do_notation)

  "_while_ C B" <= "CONST PhiSem_CF_Basic.while C B"
  "_while_ C B" <= "_while_ (_do_block C) B"
  "_while_ C B" <= "_while_ C (_do_block B)"

  "_if_ C A B" <= "CONST PhiSem_CF_Basic.if TY C A B"
  "_if_ C A B" <= "_if_ (_do_block C) A B"
  "_if_ C A B" <= "_if_ C (_do_block A) B"
  "_if_ C A B" <= "_if_ C A (_do_block B)"

  "_fix_point_ f arg B" <= "CONST op_fix_point (λf arg. B)"

end