(* * Copyright 2014, NICTA * * This software may be distributed and modified according to the terms of * the BSD 2-Clause license. Note that NO WARRANTY is provided. * See "LICENSE_BSD2.txt" for details. * * @TAG(NICTA_BSD) *) (* A theory of guarded monadic bisimulation. *) theory Bisim_UL imports "Monad_WP/NonDetMonadVCG" Corres_UL EmptyFailLib begin (* This still looks a bit wrong to me, although it is more or less what I want \ we want to be able to move hoare triples across bisimulations, and this allows guards to be left behind, more or less definition "bisim_underlying SR R P P' m m' \ \s s'. SR s s' \ (P s \ (\(r, t) \ fst (m s). \(r', t') \ fst (m' s'). R r r' \ SR t t')) \ (P' s' \ (\(r', t') \ fst (m' s'). \(r, t) \ fst (m s). R r r' \ SR t t'))" *) definition bisim_underlying :: "('a \ 'b \ bool) \ ('c \ 'd \ bool) \ ('a \ bool) \ ('b \ bool) \ ('a \ (('c \ 'a) set) \ bool) \ ('b \ (('d \ 'b) set) \ bool) \ bool" where "bisim_underlying SR R P P' m m' \ \s s'. SR s s' \ P s \ P' s' \ ((\(r, t) \ fst (m s). \(r', t') \ fst (m' s'). R r r' \ SR t t') \ (\(r', t') \ fst (m' s'). \(r, t) \ fst (m s). R r r' \ SR t t'))" (* lemma bisim_is_corres_both_ways: "bisim_underlying SR R P P' m m' = (corres_underlying SR False R P P' m m' \ corres_underlying (converse SR) False (swp R) P' P m' m)" unfolding bisim_underlying_def corres_underlying_def by (fastforce simp: swp_def Ball_def Bex_def) *) lemma bisim_valid: assumes ac: "bisim_underlying (op =) (op =) P P' a a'" and rl: "\Q\ a \S\" shows "\P and P' and Q\ a' \S\" using ac rl unfolding bisim_underlying_def valid_def by (fastforce simp: split_def) lemma bisim_valid2: assumes ac: "bisim_underlying (op =) (op =) P P' a a'" and rl: "\Q\ a' \S\" shows "\P and P' and Q\ a \S\" using ac rl unfolding bisim_underlying_def valid_def by (fastforce simp: split_def) lemma bisim_underlyingI [consumes 0, case_names Left Right]: assumes r1: "\s s' r t. \SR s s'; P s; P' s'; (r, t) \ fst (m s) \ \ \(r', t') \ fst (m' s'). R r r' \ SR t t'" and r2: "\s s' r' t'. \SR s s'; P s; P' s'; (r', t') \ fst (m' s') \ \ \(r, t) \ fst (m s). R r r' \ SR t t'" shows "bisim_underlying SR R P P' m m'" unfolding bisim_underlying_def by (fastforce dest: r1 r2 simp: split_def) lemma bisim_underlyingE1: assumes bs: "bisim_underlying SR R P P' m m'" and sr: "SR s s'" and ps: "P s" "P' s'" and ms: "(r, t) \ fst (m s)" and rl: "\r' t'. \ (r', t') \ fst (m' s'); R r r'; SR t t' \ \ X" shows X using bs sr ps ms unfolding bisim_underlying_def by (fastforce intro: rl) lemma bisim_underlyingE2: assumes bs: "bisim_underlying SR R P P' m m'" and sr: "SR s s'" and ps: "P s" "P' s'" and ms: "(r', t') \ fst (m' s')" and rl: "\r t. \ (r, t) \ fst (m s); R r r'; SR t t' \ \ X" shows X using bs sr ps ms unfolding bisim_underlying_def by (fastforce intro: rl) lemma bisim_split: assumes ac: "bisim_underlying SR R' P P' a c" and bd: "\r r'. R' r r' \ bisim_underlying SR R (Q r) (Q' r') (b r) (d r')" and v1: "\S\ a \Q\" and v2: "\S'\ c \Q'\" shows "bisim_underlying SR R (P and S) (P' and S') (a >>= b) (c >>= d)" using ac apply - apply (rule bisim_underlyingI) apply (clarsimp simp: in_monad split_def) apply (erule (4) bisim_underlyingE1) apply (frule (1) use_valid [OF _ v1]) apply (frule (1) use_valid [OF _ v2]) apply (erule (4) bisim_underlyingE1 [OF bd]) apply (rename_tac r'' t'') apply (rule_tac x = "(r'', t'')" in bexI) apply clarsimp apply (fastforce simp: in_monad) apply (clarsimp simp: in_monad split_def) apply (erule (4) bisim_underlyingE2) apply (frule (1) use_valid [OF _ v1]) apply (frule (1) use_valid [OF _ v2]) apply (erule (4) bisim_underlyingE2 [OF bd]) apply (rename_tac r'' t'') apply (rule_tac x = "(r'', t'')" in bexI) apply clarsimp apply (fastforce simp: in_monad) done abbreviation "bisim \ bisim_underlying (op =)" lemma bisim_refl: assumes rrefl: "\r. R r r" shows "bisim R P P' m m" apply (rule bisim_underlyingI) apply (clarsimp simp: split_def) apply (erule bexI [rotated]) apply (simp add: rrefl) apply (clarsimp simp: split_def) apply (erule bexI [rotated]) apply (simp add: rrefl) done lemma bisim_guard_imp: assumes bs: "bisim_underlying SR R Q Q' m m'" and rls: "\s. P s \ Q s" "\s. P' s \ Q' s" shows "bisim_underlying SR R P P' m m'" using bs rls by (fastforce intro!: bisim_underlyingI elim: bisim_underlyingE1 bisim_underlyingE2) lemma bisim_return': assumes Rxx: "R x x'" shows "bisim_underlying SR R P P' (return x) (return x')" apply (rule bisim_underlyingI) apply (clarsimp simp: in_monad split_def Bex_def Rxx) apply (clarsimp simp: in_monad split_def Bex_def Rxx) done lemmas bisim_return = bisim_return' [where P = \ and P' = \] lemma bisim_split_handle: assumes bm: "bisim (f' \ r) Pn Pn' m m'" and bc: "\x x'. f' x x' \ bisim (f \ r) (Pf x) (Pf' x') (c x) (c' x')" and v1: "\P\ m \\_ _. True\, \Pf\" and v2: "\P'\ m' \\_ _. True\, \Pf'\" shows "bisim (f \ r) (Pn and P) (Pn' and P') (m c) (m' c')" unfolding handleE_def handleE'_def apply (rule bisim_split [where Q = "\r s. case_sum (\l. Pf l s) (\_. True) r" and Q' = "\r s. case_sum (\l. Pf' l s) (\_. True) r", OF bm, folded validE_def]) apply (case_tac ra) apply clarsimp apply (erule bc) apply clarsimp apply (rule bisim_return') apply simp apply (rule v1) apply (rule v2) done (* Set up wpc *) lemma wpc_helper_bisim: "bisim_underlying SR r Q Q' f f' \ wpc_helper (P, P') (Q, {s. Q' s}) (bisim_underlying SR r P (\s. s \ P') f f')" apply (clarsimp simp: wpc_helper_def) apply (erule bisim_guard_imp) apply simp apply fastforce done wpc_setup "\m. bisim_underlying SR r P P' a m" wpc_helper_bisim wpc_setup "\m. bisim_underlying SR r P P' a (m >>= c)" wpc_helper_bisim lemma bisim_split_refl: assumes bs: "\r. bisim R (Q r) (Q' r) (b r) (d r)" and v1: "\S\ a \Q\" and v2: "\S'\ a \Q'\" shows "bisim R S S' (a >>= b) (a >>= d)" apply (rule bisim_guard_imp) apply (rule bisim_split [OF _ _ v1 v2]) apply (rule bisim_refl [where P = \ and P' = \ and R = "op =", OF refl]) apply simp apply (rule bs) apply simp_all done lemma bisim_throwError': "f e e' \ bisim_underlying SR (f \ R') P P' (throwError e) (throwError e')" apply (rule bisim_underlyingI) apply (clarsimp simp: in_monad Bex_def)+ done lemmas bisim_throwError = bisim_throwError' [where P = \ and P' = \] lemma bisim_splitE: assumes ac: "bisim_underlying SR (f \ R') P P' a c" and bd: "\r r'. R' r r' \ bisim_underlying SR (f \ R) (Q r) (Q' r') (b r) (d r')" and v1: "\S\ a \Q\, -" and v2: "\S'\ c \Q'\, -" shows "bisim_underlying SR (f \ R) (P and S) (P' and S') (a >>=E b) (c >>=E d)" apply (simp add: bindE_def lift_def[abs_def]) apply (rule bisim_split [where Q = "\r s. case_sum (\_. True) (\l. Q l s) r" and Q' = "\r s. case_sum (\_. True) (\l. Q' l s) r", OF ac, folded validE_def, folded validE_R_def]) apply (case_tac r') apply clarsimp apply (erule bisim_throwError') apply clarsimp apply (erule bd) apply (rule v1) apply (rule v2) done lemma bisim_split_reflE: assumes ab: "\r. bisim (f \ R) (Q r) (Q' r) (a r) (b r)" and v1: "\S\ m \Q\, -" and v2: "\S'\ m \Q'\, -" and refls: "\e. f e e" "\r. R r r" shows "bisim (f \ R) S S' (m >>=E a) (m >>=E b)" using refls apply - apply (rule bisim_guard_imp) apply (rule bisim_splitE [where R' = "op =", OF _ _ v1 v2]) apply (rule bisim_refl) apply (case_tac r, simp_all)[1] apply simp apply (rule ab) apply simp+ done lemma bisim_split_bind_case_sum: "\bisim_underlying sr (lr \ rr) P P' a d; \rv rv'. lr rv rv' \ bisim_underlying sr r (R rv) (R' rv') (b rv) (e rv'); \rv rv'. rr rv rv' \ bisim_underlying sr r (S rv) (S' rv') (c rv) (f rv'); \Q\ a \S\, \R\; \Q'\ d \S'\, \R'\\ \ bisim_underlying sr r (P and Q) (P' and Q') (a >>= case_sum b c) (d >>= case_sum e f)" apply (erule bisim_split [where Q = "\rv s. case_sum (\l. R l s) (\r. S r s) rv" and Q' = "\rv s. case_sum (\l. R' l s) (\r. S' r s) rv", folded validE_def]) apply (case_tac r') apply clarsimp apply clarsimp apply assumption+ done lemma bisim_liftE [simp]: "bisim_underlying SR (f \ R) P P' (liftE a) (liftE b) = bisim_underlying SR R P P' a b" by (fastforce simp: in_monad intro: bisim_underlyingI elim: bisim_underlyingE1 bisim_underlyingE2) lemma bisim_when: assumes bs: "b \ bisim_underlying SR R P P' m m'" and rr: "R () ()" shows "bisim_underlying SR R (\s. b \ P s) (\s. b \ P' s) (when b m) (when b m')" using assms apply (cases b, simp_all add: when_def) apply (erule bisim_return) done (* not really used *) definition "det_on P f \ \s. P s \ (\r. f s = ({r}, False))" lemma det_onE: "\det_on P f; P s; \r s'. \ (r, s') \ fst (f s); \ snd (f s)\ \ R \ \ R" unfolding det_on_def by fastforce lemma bisim_noop_det_on: assumes a: "\s. \Pa and op = s\ a \\_. op = s\" and b: "\s. \Pb and op = s\ b \\_. op = s\" and da: "det_on P a" and db: "det_on P' b" shows "bisim_underlying sr dc (Pa and P) (Pb and P') a b" using da db apply - apply (rule bisim_underlyingI) apply clarsimp apply (erule (1) det_onE)+ apply (frule use_valid [OF _ a], fastforce) apply (frule use_valid [OF _ b], fastforce) apply fastforce apply clarsimp apply (erule (1) det_onE)+ apply (frule use_valid [OF _ a], fastforce) apply (frule use_valid [OF _ b], fastforce) apply fastforce done lemma det_on_gets: "det_on \ (gets f)" unfolding det_on_def by (clarsimp simp: gets_def return_def bind_def get_def) lemma hoare_gen_asmE': "(P \ \P'\ f \Q\, \R\) \ \P' and K P\ f \Q\, \R\" unfolding validE_def by (erule hoare_gen_asm) lemma det_onE': "\det_on P f; P s; \r s'. \ f s = ({(r, s')}, False)\ \ R \ \ R" unfolding det_on_def by fastforce (* ugh *) lemma det_on_guard_imp [wp_comb]: assumes da: "det_on P' a" and "\s. P s \ P' s" shows "det_on P a" using assms unfolding det_on_def by auto lemma det_on_split [wp_split]: assumes da: "det_on Pa a" and db: "\x. det_on (Pb x) (b x)" and v: "\Pb'\ a \Pb\" shows "det_on (Pa and Pb') (a >>= b)" unfolding det_on_def using da apply - apply clarsimp apply (erule (1) det_onE) apply (frule (1) use_valid [OF _ v]) apply (erule det_onE' [OF da]) apply (erule det_onE' [OF db]) apply (clarsimp simp: bind_def split_def) done lemma det_det_on: "det m \ det_on \ m" unfolding det_def det_on_def by auto lemma det_on_liftE [wp]: "det_on P m \ det_on P (liftE m)" unfolding liftE_def apply (rule det_on_guard_imp) apply (erule det_on_split [OF _ det_det_on]) apply simp apply wp apply simp done lemma det_on_lift [wp]: "(\y. det_on (P y) (m y)) \ det_on (case_sum (\_. \) P x) (lift m x)" unfolding lift_def by (auto simp: det_on_def throwError_def return_def split: sum.splits) lemma det_on_assert_opt [wp]: "det_on (\_. x \ None) (assert_opt x)" unfolding det_on_def assert_opt_def by (fastforce split: option.splits simp: fail_def return_def) lemmas dets_to_det_on [wp] = det_det_on [OF det_gets] det_det_on [OF return_det] (* Set up wpc *) lemma wpc_helper_det_on: "det_on Q f \ wpc_helper (P, P') (Q, Q') (det_on P f)" apply (clarsimp simp: wpc_helper_def det_on_def) done wpc_setup "\m. det_on P m" wpc_helper_det_on wpc_setup "\m. det_on P (m >>= c)" wpc_helper_det_on lemma bisim_symb_exec_r_det_on: assumes z: "\rv. bisim_underlying sr r P (Q' rv) x (y rv)" assumes y: "\P'\ m \Q'\" assumes x: "\s. \Pe and op = s\ m \\r. op = s\" assumes nf: "det_on Pd m" shows "bisim_underlying sr r P (P' and Pe and Pd) x (m >>= (\rv. y rv))" apply (rule bisim_guard_imp) apply (subst gets_bind_ign [symmetric], rule bisim_split) apply (rule bisim_noop_det_on [OF _ x det_on_gets]) apply wp apply fastforce apply (rule nf) apply (rule z) apply (wp y) apply simp+ done definition not_empty :: "('a \ bool) \ ('a \ 'b set \ bool) \ bool" where "not_empty P f \ \s. P s \ (fst (f s) \ {})" lemma not_emptyE: "\ not_empty P f; P s; \r s'. \ (r, s') \ fst (f s)\ \ R \ \ R" unfolding not_empty_def by fastforce (* ugh *) lemma not_empty_guard_imp [wp_comb]: assumes da: "not_empty P' a" and "\s. P s \ P' s" shows "not_empty P a" using assms unfolding not_empty_def by auto lemma not_empty_split [wp_split]: assumes da: "not_empty Pa a" and db: "\x. not_empty (Pb x) (b x)" and v: "\Pb'\ a \Pb\" shows "not_empty (Pa and Pb') (a >>= b)" unfolding not_empty_def using da apply - apply clarsimp apply (erule (1) not_emptyE) apply (frule (1) use_valid [OF _ v]) apply (erule not_emptyE [OF da]) apply (erule not_emptyE [OF db]) apply (fastforce simp: bind_def split_def) done lemma not_empty_return [wp]: "not_empty \ (return x)" unfolding not_empty_def by (simp add: return_def) lemma not_empty_liftE [wp]: assumes ne: "not_empty P m" shows "not_empty P (liftE m)" unfolding liftE_def apply (rule not_empty_guard_imp) apply (wp ne) apply simp done lemma not_empty_lift [wp]: "(\y. not_empty (P y) (m y)) \ not_empty (case_sum (\_. \) P x) (lift m x)" unfolding lift_def by (auto simp: not_empty_def throwError_def return_def split: sum.splits) lemma not_empty_assert_opt [wp]: "not_empty (\_. x \ None) (assert_opt x)" unfolding not_empty_def assert_opt_def by (fastforce split: option.splits simp: fail_def return_def) lemma not_empty_gets [wp]: "not_empty \ (gets f)" unfolding not_empty_def by (clarsimp simp: gets_def return_def bind_def get_def) (* Set up wpc *) lemma wpc_helper_not_empty: "not_empty Q f \ wpc_helper (P, P') (Q, Q') (not_empty P f)" apply (clarsimp simp: wpc_helper_def not_empty_def) done wpc_setup "\m. not_empty P m" wpc_helper_not_empty wpc_setup "\m. not_empty P (m >>= c)" wpc_helper_not_empty lemma bisim_noop: assumes a: "\s. \Pa and op = s\ a \\_. op = s\" and b: "\s. \Pb and op = s\ b \\_. op = s\" and da: "not_empty P a" and db: "not_empty P' b" shows "bisim_underlying sr dc (Pa and P) (Pb and P') a b" using da db apply - apply (rule bisim_underlyingI) apply clarsimp apply (erule (1) not_emptyE)+ apply (frule use_valid [OF _ a], fastforce) apply (frule use_valid [OF _ b], fastforce) apply fastforce apply clarsimp apply (erule (1) not_emptyE)+ apply (frule use_valid [OF _ a], fastforce) apply (frule use_valid [OF _ b], fastforce) apply fastforce done lemma bisim_symb_exec_r: assumes z: "\rv. bisim_underlying sr r P (Q' rv) x (y rv)" assumes y: "\P'\ m \Q'\" assumes x: "\s. \Pe and op = s\ m \\r. op = s\" assumes ne: "not_empty Pd m" shows "bisim_underlying sr r P (P' and Pe and Pd) x (m >>= (\rv. y rv))" apply (rule bisim_guard_imp) apply (subst gets_bind_ign [symmetric], rule bisim_split) apply (rule bisim_noop [OF _ x not_empty_gets]) apply wp apply fastforce apply (rule ne) apply (rule z) apply (wp y) apply simp+ done lemma bisim_not_empty: assumes bs: "bisim r P P' m m'" and ne: "not_empty R m" shows "not_empty (P and P' and R) m'" unfolding not_empty_def using bs ne apply clarsimp apply (erule (1) not_emptyE) apply (erule_tac s=s and s'=s in bisim_underlyingE1 [where SR = "op ="]) apply simp+ done lemma bisim_split_req: assumes ac: "bisim (op =) P P' a c" and bd: "\r. bisim R (Q r) (Q' r) (b r) (d r)" and v1: "\S\ a \\r. Q r and Q' r\" shows "bisim R (P and S) P' (a >>= b) (c >>= d)" using ac apply - apply (rule bisim_underlyingI) apply (clarsimp simp: in_monad split_def) apply (erule bisim_underlyingE1) apply simp apply assumption+ apply (frule (1) use_valid [OF _ v1]) apply clarsimp apply (rule bisim_underlyingE1 [OF bd]) apply simp apply assumption+ apply (rename_tac r'' t'') apply (rule_tac x = "(r'', t'')" in bexI) apply clarsimp apply (fastforce simp: in_monad) apply (clarsimp simp: in_monad split_def) apply (erule bisim_underlyingE2) apply simp apply assumption+ apply (frule (1) use_valid [OF _ v1]) apply clarsimp apply (rule bisim_underlyingE2 [OF bd]) apply simp apply assumption+ apply (rename_tac r'' t'') apply (rule_tac x = "(r'', t'')" in bexI) apply clarsimp apply (fastforce simp: in_monad) done lemma bisim_splitE_req: assumes ac: "bisim (f \ op =) P P' a c" and bd: "\r. bisim (f \ R) (Q r) (Q' r) (b r) (d r)" and v1: "\S\ a \\r. Q r and Q' r\, -" shows "bisim (f \ R) (P and S) P' (a >>=E b) (c >>=E d)" using ac apply - apply (simp add: bindE_def lift_def[abs_def]) apply (rule bisim_underlyingI) apply (clarsimp simp: in_monad split_def) apply (erule bisim_underlyingE1) apply simp apply assumption+ apply (frule (1) use_valid [OF _ v1 [unfolded validE_R_def validE_def]]) apply clarsimp apply (case_tac x) apply (clarsimp simp: in_monad) apply (rule_tac x = "(Inl y', t')" in bexI) apply fastforce apply (fastforce simp: in_monad) apply clarsimp apply (rule bisim_underlyingE1 [OF bd]) apply simp apply assumption+ apply (rename_tac r'' t'') apply (rule_tac x = "(r'', t'')" in bexI) apply clarsimp apply (fastforce simp: in_monad) apply (clarsimp simp: in_monad split_def) apply (erule bisim_underlyingE2) apply simp apply assumption+ apply (frule (1) use_valid [OF _ v1 [unfolded validE_R_def validE_def]]) apply clarsimp apply (case_tac r) apply (clarsimp simp: in_monad) apply (rule_tac x = "(Inl aa, s'')" in bexI) apply fastforce apply (fastforce simp: in_monad) apply clarsimp apply (rule bisim_underlyingE2 [OF bd]) apply simp apply assumption+ apply (rename_tac r'' t'') apply (rule_tac x = "(r'', t'')" in bexI) apply clarsimp apply (fastforce simp: in_monad) done lemma bisim_symb_exec_r_bs: assumes bs: "bisim op = R R' (return ()) m" and z: "\rv. bisim r P P' x (y rv)" shows "bisim r (P and R and P') R' x (m >>= (\rv. y rv))" apply (rule bisim_guard_imp) apply (subst return_bind [symmetric, where f = "\(_ :: unit).x"], rule bisim_split_req) apply (rule bs) apply (rule z) apply wp apply simp apply simp+ done end