Theory Refinement

(*<*)
theory Refinement
imports
  Constructions
  Next_Imp
  Stability
begin

(*>*)
section‹ Refinement\label{sec:refinement} ›

text‹

We develop a refinement story for the typ('a, 's, 'v) spec lattice.

References:
  citet"Vafeiadis:2008" (RGsep, program logic) and citet"LiangFenFu:2014" (RGsim, refinement)
  citet"ArmstrongGomesStruth:2014"
  citet"vanStaden:2015"

definition refinement :: "'s pred  ('a, 's, 'v) spec  ('a, 's, 'v) spec  ('v  's pred)  ('a, 's, 'v) spec" ("_⦄, _  _, _" [0,0,0,0] 100) where
  "P⦄, A  G, Q = spec.pre P  A + G  spec.post Q"

text‹

An intuitive gloss on the proposition c ≤ ⦃P⦄, A ⊩ G, ⦃Q⦄› is: assuming the precondition P› holds
and all steps conform to the process A›, then c› is a refinement of G› and satisfies
the postcondition Q›.

Observations:
  We use constnext_imp here;constheyting is (only) enough for an assume/guarantee program logic (see \S\ref{sec:refinement-ag})
  A› is arbitrary but is intended to constrain only constenv steps
   similarly termination can depend on A›: a parallel composition can only terminate if all of its constituent processes terminate
  As P + Q› implies idle ≤ Q›, in practice idle ≤ G›
  see \S\ref{sec:programs-refinement-intros} for some introduction rules

›

setup Sign.mandatory_path "refinement"

lemma E:
  assumes "c  P⦄, A  G, Q"
  obtains "c  spec.pre P  A + G"
      and "c  spec.pre P  A + spec.post Q"
using assms by (simp add: refinement_def spec.next_imp.infR)

lemma pre_post_cong:
  assumes "P = P'"
  assumes "Q = Q'"
  shows "P⦄, A  G, Q = P'⦄, A  G, Q'"
using assms by simp

lemma top:
  shows "P⦄, A  ,  = "
    and "P⦄, A  ,  = "
    and "P⦄, A  , λ_ _. True = "
by (simp_all add: refinement_def)

lemma mcont2mcont[cont_intro]:
  assumes "mcont luba orda Sup (≤) G"
  shows "mcont luba orda Sup (≤) (λx. P⦄, A  G x, Q)"
by (simp add: assms refinement_def)

lemma mono:
  assumes "P'  P"
  assumes "A'  A"
  assumes "G  G'"
  assumes "Q  Q'"
  shows "P⦄, A  G, Q  P'⦄, A'  G', Q'"
unfolding refinement_def
apply (strengthen ord_to_strengthen(1)[OF assms(1)])
apply (strengthen ord_to_strengthen(1)[OF assms(2)])
apply (strengthen ord_to_strengthen(1)[OF assms(3)])
apply (strengthen ord_to_strengthen(1)[OF assms(4)])
apply simp
done

lemma strengthen[strg]:
  assumes "st_ord (¬ F) P P'"
  assumes "st_ord (¬ F) A A'"
  assumes "st_ord F G G'"
  assumes "st_ord F Q Q'"
  shows "st_ord F (P⦄, A  G, Q) (P'⦄, A'  G', Q')"
using assms by (cases F; simp add: refinement.mono)

lemma mono_stronger:
  assumes "P'  P"
  assumes "spec.pre P'  A'  A"
  assumes "spec.pre P'  G  A' + G'"
  assumes "Q  Q'"
  assumes "spec.idle  G'"
  shows "P⦄, A  G, Q  P'⦄, A'  G', Q'"
unfolding refinement_def
apply (strengthen ord_to_strengthen(2)[OF assms(1)])
apply (strengthen ord_to_strengthen(2)[OF assms(2)])
apply (strengthen ord_to_strengthen(2)[OF assms(4)])
apply (simp add: spec.next_imp.infR)
apply (metis assms(3) heyting.commute le_infI1
             spec.next_imp.closure.cl spec.pre.next_imp_eq_heyting(2)[OF assms(5)])
done

lemmas pre_ag = order.trans[OF _ refinement.mono[OF order.refl _ _ order.refl], of c] for c
lemmas pre_a = refinement.pre_ag[OF _ _ order.refl]
lemmas pre_g = refinement.pre_ag[OF _ order.refl]

lemma pre:
  assumes "c  P⦄, A  G, Q"
  assumes "s. P' s  P s"
  assumes "A'  A"
  assumes "G  G'"
  assumes "s v. Q s v  Q' s v"
  shows "c  P'⦄, A'  G', Q'"
using assms(2-) by (blast intro: order.trans[OF assms(1) refinement.mono])

lemmas pre_pre_post = refinement.pre[OF _ _ order.refl order.refl, of c] for c

lemma pre_imp:
  assumes "s. P s  P' s"
  assumes "c  P'⦄, A  G, Q"
  shows "c  P⦄, A  G, Q"
using assms refinement.pre by blast

lemmas pre_pre = refinement.pre_imp[rotated]

lemma post_imp:
  assumes "v s. Q v s  R v s"
  assumes "c  P⦄, A  G, Q"
  shows "c  P⦄, A  G, R"
using assms refinement.pre by blast

lemmas pre_post = refinement.post_imp[rotated]
lemmas strengthen_post = refinement.pre_post

lemma pre_inf_assume:
  shows "P⦄, A  G, Q = P⦄, A  spec.pre P  G, Q"
by (simp add: refinement_def ac_simps)

lemma pre_assume_absorb:
  assumes "A  spec.pre P"
  shows "P⦄, A  G, Q = ⦄, A  G, Q"
by (simp add: assms refinement_def inf_absorb2)

lemmas sup = sup_least[where x="P⦄, A  G, Q"] for A G P Q

lemma
  shows supRL: "c  P⦄, A  G1, Q  c  P⦄, A  G1  G2, Q"
    and supRR: "c  P⦄, A  G2, Q  c  P⦄, A  G1  G2, Q"
by (simp_all add: refinement.pre_g)

lemma infR_conv:
  shows "P⦄, A  G1  G2, Q1  Q2 = P⦄, A  G1, Q1  P⦄, A  G2, Q2"
by (simp add: refinement_def ac_simps spec.next_imp.infR spec.post.inf)

lemma inf_le:
  shows "X  P⦄, A  G, Q  P⦄, X  A  X  G, Q"
by (simp add: refinement_def le_infI1 le_infI2
              spec.next_imp.infR spec.next_imp.mono spec.next_imp.contains)

lemma heyting_le:
  shows "P⦄, A  B  B H G, Q  B H P⦄, A  G, Q"
by (simp add: refinement_def ac_simps heyting.infR heyting.commute
              spec.next_imp.heytingL_distrib spec.next_imp.mono)

lemma heyting_pre:
  assumes "spec.idle  G"
  shows "spec.pre P H P'⦄, A  G, Q = P  P'⦄, A  G, Q"
by (simp add: ac_simps refinement_def spec.pre.conj assms spec.idle.post_le
        flip: spec.pre.next_imp_eq_heyting)

lemma sort_of_refl:
  assumes "c  P⦄, A  G, Q"
  shows "c  P⦄, A  c, Q"
using assms by (simp add: refinement_def spec.next_imp.infR spec.next_imp.closure.expansive)

lemma gen_asm_base:
  assumes "P  c  P'  P''⦄, A  G, Q"
  assumes "spec.idle  G"
  shows "c  P'  P  P''⦄, A  G, Q"
using assms by (simp add: refinement_def spec.pre.conj spec.pre.K spec.next_imp.botL spec.idle.post_le)

lemmas gen_asm =
  refinement.gen_asm_base[where P'="True" and P''="True", simplified]
  refinement.gen_asm_base[where P'="True", simplified]
  refinement.gen_asm_base[where P''="True", simplified]
  refinement.gen_asm_base

lemma post_conj:
  assumes "c  P⦄, A  G, Q"
  assumes "c  P⦄, A  G, Q'"
  shows "c  P⦄, A  G, λrv. Q rv  Q' rv"
using assms unfolding refinement_def by (simp add: spec.post.conj spec.next_imp.infR)

lemma conj_lift:
  assumes "c  P⦄, A  G, Q"
  assumes "c  P'⦄, A  G, Q'"
  shows "c  P  P'⦄, A  G, λrv. Q rv  Q' rv"
using assms by (blast intro: refinement.post_conj refinement.pre)

lemma drop_imp:
  assumes "c  P⦄, A  G, Q"
  shows "c  P⦄, A  G, λrv. Q' rv  Q rv"
using assms refinement.strengthen_post by fastforce

lemma "prop":
  shows "c  P⦄, A  c, λv. P"
by (simp add: refinement.sort_of_refl[where G=] refinement.gen_asm refinement.top)

lemma name_pre_state:
  assumes "s. P s  c  (=) s⦄, A  G, Q"
  assumes "spec.idle  G"
  shows "c  P⦄, A  G, Q" (is "?lhs  ?rhs")
proof -
  have "σ  G  σ  spec.post Q"
    if "σ  c"
   and "σ''<σ. σ''  spec.pre P  σ''  A"
   for σ
  proof(cases "trace.rest σ = []  trace.term σ = None")
    case True with spec.idle  G show ?thesis
      by (cases σ) (simp add: spec.singleton.le_conv order.trans[OF spec.idle.minimal_le])
  next
    case False with order.trans[OF σ  c assms(1)[where s="trace.init σ"]] that(2)
    show ?thesis
      by (cases σ)
         (clarsimp simp: refinement_def spec.singleton.next_imp_le_conv spec.singleton.le_conv;
          fastforce simp: trace.less dest: spec[where x="trace.T (trace.init σ) [] None"])
  qed
  then show ?thesis
    by - (rule spec.singleton_le_extI;
          auto simp: refinement_def spec.singleton.next_imp_le_conv
              intro: order.trans[OF spec.singleton.mono])
qed

setup Sign.parent_path


subsection‹ Geenral rules for the ‹('a, 's, 'v) spec› lattice\label{sec:refinement-spec} ›

setup Sign.mandatory_path "spec"

setup Sign.mandatory_path "term"

setup Sign.mandatory_path "all"

lemma refinement:
  shows "spec.term.all (P⦄, A  G, Q) = P⦄, spec.term.all A  spec.term.all G, "
by (simp add: refinement_def spec.term.all.next_imp spec.term.all.inf spec.term.all.pre spec.term.all.post)

setup Sign.parent_path

setup Sign.mandatory_path "none"

lemma refinement_le:
  shows "spec.term.none (P⦄, A  G, Q)  P⦄, spec.term.all A  spec.term.all G, "
by (simp add: spec.term.galois spec.term.all.refinement order.trans[OF spec.term.all.expansive])

setup Sign.parent_path

setup Sign.parent_path

setup Sign.mandatory_path "invmap"

lemma refinement:
  fixes af :: "'a  'b"
  fixes sf :: "'s  't"
  fixes vf :: "'v  'w"
  fixes A :: "('b, 't, 'w) spec"
  fixes G :: "('b, 't, 'w) spec"
  fixes P :: "'t pred"
  fixes Q :: "'w  't pred"
  shows "spec.invmap af sf vf (P⦄, A  G, Q)
      = (λs. P (sf s)⦄, spec.invmap af sf vf A  spec.invmap af sf vf G, λv s. Q (vf v) (sf s))"
unfolding refinement_def
by (simp only: spec.next_imp.invmap spec.invmap.inf spec.invmap.pre spec.invmap.post)

setup Sign.parent_path

setup Sign.parent_path


subsubsection‹ Actions\label{sec:refinement-action} ›

text‹

Actions are anchored at the start of a trace, and therefore must be an initial step of the
assume A›. However by the semantics of constspec.next_imp we may only know
that that initial state of the trace is acceptable to A›
when showing that F›-steps are F'›-steps
(the second assumption). This hypothesis is vacuous when idle ≤ A›.

›

setup Sign.mandatory_path "refinement.spec"

lemma action:
  fixes F :: "('v × 'a × 's × 's) set"
  assumes "v a s s'. P s; (v, a, s, s')  F; (a, s, s')  spec.initial_steps A  Q v s'"
  assumes "v a s s'. P s; (v, a, s, s')  F; (a, s, s)  spec.initial_steps A  (v, a, s, s')  F'"
  shows "spec.action F  P⦄, A  spec.action F', Q"
proof -
  have "spec.action (F  UNIV × UNIV × Pre P)  A + spec.action F'  spec.post Q"
  proof(induct rule: spec.action_le)
    case idle show ?case
      by (simp add: spec.next_imp.contains spec.idle.action_le spec.idle.post_le)
  next
    case (step v a s s') then show ?case
      by (fastforce simp: spec.singleton.next_imp_le_conv trace.split_all spec.initial_steps_def
                          trace.less Cons_eq_append_conv spec.singleton.post_le_conv
                          order.trans[OF spec.idle.minimal_le spec.idle.action_le]
                    elim: assms trace.less_eqE prefixE
                   intro: spec.action.stepI)
  qed
  then show ?thesis
    by (simp add: refinement_def spec.pre.next_imp_eq_heyting spec.idle.post_le spec.idle.action_le
                  heyting order.trans[OF spec.pre.inf_action_le(2)])
qed

lemma return:
  shows "spec.return v  Q v⦄, A  spec.return v, Q"
by (auto simp: spec.return_def intro: refinement.spec.action)

lemma action_rel:
  fixes F :: "('v × 'a × 's × 's) set"
  assumes "v a s s'. P s; (v, a, s, s')  F; (a, s, s')  spec.initial_steps A  Q v s'"
  assumes "v a s s'. P s; (v, a, s, s')  F; (a, s, s)  spec.initial_steps A; s  s'  (a, s, s')  r"
  shows "spec.action F  P⦄, A  spec.rel r, Q"
by (force simp: spec.rel_def spec.rel.act_def spec.term.all.action
         intro: assms refinement.supRL refinement.spec.action
                refinement.pre_g[OF _ spec.term.all.mono[OF spec.kleene.expansive_star]])

setup Sign.parent_path


subsubsection‹ Bind\label{sec:refinement-bind} ›

text‹

Consider showing f ⤜ g ≤ f' ⤜ g'› under the assume A› and pre/post conditions P›/Q›.

The tricky part is to residuate the assume A› wrt the process f› for use in the refinement proof of g›.
  we want to preserve as much of the structure of A› as possible
  intuition: we want all the ways a trace can continue in A› having started with a terminating trace in f›
  intuitively a right residual for constspec.bind should do the job
   however unlike citet"HoareHe:1987" we have no chance of a right residual for constspec.bind as we use traces (they use relations)
    i.e., if it is not the case that f ⤜ ⊥ ≤ A› then there is no continuation h› such that f ⤜ h ≤ A›.
    also such a residual h› has arbitrary behavior starting from states that f› cannot reach
     i.e., for traces ¬σ ≤ f› ⦉σ⦊ ⪢ h ≤ A› need not hold
     and the user-provided assertions may be too weak to correct for this
  in practice the termination information in the assume A› is not useful

We therefore define an ad hoc residual that does the trick.

See citet‹\S4› in "Emerson:1983" for some related concerns.

›

setup Sign.mandatory_path "refinement.spec.bind"

definition res :: "('a, 's, 'v) spec  ('a, 's, 'w) spec  'v  ('a, 's, 'w) spec" where
  "res f A v = {trace.final' s xs, ys, w |s xs ys w. s, xs, Some v  f  s, xs @ ys, None  A}"

setup Sign.parent_path

setup Sign.mandatory_path "spec.singleton.refinement.bind"

lemma res_le_conv[spec.singleton.le_conv]:
  shows "σ  refinement.spec.bind.res f A v
     (s xs. s, xs, Some v  f
               trace.init σ = trace.final' s xs
               s, xs @ trace.rest σ, None  A)" (is "?lhs  ?rhs")
proof(rule iffI)
  show "?lhs  ?rhs"
    by (fastforce simp: refinement.spec.bind.res_def trace.split_Ex spec.singleton_le_conv
                        trace.less_eq_None trace.natural'.append trace.natural_def
                  elim: trace.less_eqE order.trans[rotated])
  show "?rhs  ?lhs"
    by (cases σ) (clarsimp simp: refinement.spec.bind.res_def; blast)
qed

setup Sign.parent_path

setup Sign.mandatory_path "spec.term.none.refinement.bind"

lemma resL:
  shows "refinement.spec.bind.res (spec.term.none f) A v = "
by (simp add: refinement.spec.bind.res_def spec.singleton.le_conv)

lemma resR:
  shows "refinement.spec.bind.res f (spec.term.none A) v = refinement.spec.bind.res f A v"
by (simp add: refinement.spec.bind.res_def spec.singleton.le_conv)

setup Sign.parent_path

setup Sign.mandatory_path "spec.term.all.refinement.bind"

lemma resR_mono:
  shows "refinement.spec.bind.res f (spec.term.all A) v = refinement.spec.bind.res f A v"
by (simp add: refinement.spec.bind.res_def spec.singleton.le_conv)
   (meson dual_order.trans spec.singleton.less_eq_None)

lemma res:
  shows "spec.term.all (refinement.spec.bind.res f A v) = refinement.spec.bind.res f A v"
by (rule spec.singleton.antisym) (auto simp: spec.singleton.le_conv)

setup Sign.parent_path

setup Sign.mandatory_path "spec.term.closed.refinement.bind"

lemma res:
  shows "refinement.spec.bind.res f A v  spec.term.closed _"
by (subst spec.term.all.refinement.bind.res[symmetric]) (rule spec.term.all.closed)

setup Sign.parent_path

setup Sign.mandatory_path "refinement.spec.bind.res"

lemma bot:
  shows botL: "refinement.spec.bind.res  = "
    and botR: "refinement.spec.bind.res f  = "
by (simp_all add: refinement.spec.bind.res_def fun_eq_iff spec.singleton.not_bot)

lemma mono:
  assumes "f  f'"
  assumes "A  A'"
  shows "refinement.spec.bind.res f A v  refinement.spec.bind.res f' A' v"
using assms unfolding refinement.spec.bind.res_def by (fastforce intro!: Sup_subset_mono)

lemma strengthen[strg]:
  assumes "st_ord F f f'"
  assumes "st_ord F A A'"
  shows "st_ord F (refinement.spec.bind.res f A v) (refinement.spec.bind.res f' A' v)"
using assms by (cases F; simp add: refinement.spec.bind.res.mono)

lemma mono2mono[cont_intro, partial_function_mono]:
  assumes "monotone orda (≤) f"
  assumes "monotone orda (≤) A"
  shows "monotone orda (≤) (λx. refinement.spec.bind.res (f x) (A x) v)"
using assms by (simp add: monotone_def refinement.spec.bind.res.mono)

lemma SupL:
  shows "refinement.spec.bind.res (X) A v = (xX. refinement.spec.bind.res x A v)"
by (rule antisym; simp add: refinement.spec.bind.res_def; blast)

lemma SupR:
  shows "refinement.spec.bind.res f (X) v = (xX. refinement.spec.bind.res f x v)"
by (rule antisym; simp add: refinement.spec.bind.res_def; blast)

lemma InfL_le:
  shows "refinement.spec.bind.res (X) A v  (xX. refinement.spec.bind.res x A v)"
by (simp add: refinement.spec.bind.res_def le_Inf_iff) fast

lemma InfR_le:
  shows "refinement.spec.bind.res f (X) v  (xX. refinement.spec.bind.res f x v)"
by (simp add: refinement.spec.bind.res_def le_Inf_iff) fast

lemma mcont2mcont[cont_intro]:
  assumes "mcont luba orda Sup (≤) f"
  assumes "mcont luba orda Sup (≤) A"
  shows "mcont luba orda Sup (≤) (λx. refinement.spec.bind.res (f x) (A x) v)"
proof(rule ccpo.mcont2mcont'[OF complete_lattice_ccpo _ _ assms(1)])
  show "mcont Sup (≤) Sup (≤) (λf. refinement.spec.bind.res f (A x) v)" for x
    by (intro mcontI contI monotoneI)
       (simp_all add: refinement.spec.bind.res.mono refinement.spec.bind.res.SupL)
  show "mcont luba orda Sup (≤) (λx. refinement.spec.bind.res f (A x) v)" for f
    by (intro mcontI monotoneI contI)
       (simp_all add: mcont_monoD[OF assms(2)] refinement.spec.bind.res.mono contD[OF mcont_cont[OF assms(2)]]
                      refinement.spec.bind.res.SupR image_image)
qed

lemma returnL:
  assumes "spec.idle  A"
  shows "refinement.spec.bind.res (spec.return v) A v = spec.term.all A" (is "?lhs = ?rhs")
proof(rule antisym[OF _ spec.singleton_le_extI])
  show "?lhs  ?rhs"
    by (auto simp: refinement.spec.bind.res_def trace.split_all spec.singleton.le_conv)
  show "σ  ?lhs" if "σ  ?rhs" for σ
    using that
    by (auto simp: spec.singleton.le_conv
           intro!: exI[where x="trace.init σ"] exI[where x="[]"]
             elim: order.trans[rotated])
qed

lemma rel_le:
  assumes "r  r'"
  shows "refinement.spec.bind.res f (spec.rel r) v  spec.rel r'"
using assms by (force intro: spec.singleton_le_extI simp: spec.singleton.le_conv trace.steps'.append)

setup Sign.parent_path

setup Sign.mandatory_path "spec.steps.refinement.spec.bind"

lemma res_le: ―‹ we can always discard the extra structure ›
  shows "spec.steps (refinement.spec.bind.res f A v)  spec.steps A"
by (meson order_trans refinement.spec.bind.res.mono refinement.spec.bind.res.rel_le
          spec.rel.galois spec.rel.upper_lower_expansive)

setup Sign.parent_path


text‹

A refinement rule for constspec.bind.
The function vf› abstracts interstitial monadic return values.

›

setup Sign.mandatory_path "refinement.spec"

lemma bind_abstract:
  fixes f :: "('a, 's, 'v) spec"
  fixes f' :: "('a, 's, 'v') spec"
  fixes g :: "'v  ('a, 's, 'w) spec"
  fixes g' :: "'v'  ('a, 's, 'w) spec"
  fixes vf :: "'v  'v'"
  assumes g: "v. g v  Q' (vf v)⦄, refinement.spec.bind.res (spec.pre P  spec.term.all A  f') A (vf v)  g' (vf v), Q"
  assumes f: "f  P⦄, spec.term.all A  spec.vinvmap vf f', λv. Q' (vf v)"
  shows "f  g  P⦄, A  f'  g', Q"
proof (rule order.trans[OF spec.bind.mono[OF f g]],
       unfold refinement_def spec.bind.inf_post,
       induct rule: spec.bind_le)
  case incomplete show ?case
    apply (simp add: spec.term.galois spec.term.all.next_imp spec.term.all.bind spec.term.all.inf
                     spec.term.all.post spec.term.all.pre)
    apply (simp add: spec.next_imp.mono[OF order.refl] le_supI1 le_infI1 spec.term.none.invmap
                     spec.invmap.id
               flip: spec.term.galois)
    done
next
  case (continue σf σg v)
  have "s, xs, w  f'  (λv. g' v  spec.post Q)"
    if le: "trace.T s xs w  trace.T (trace.init σf) (trace.rest σf @ trace.rest σg) (trace.term σg)"
   and pre: "σ''<trace.T s xs w. σ''  spec.pre P  A"
   for s xs w
    using le
  proof(induct rule: trace.less_eq_extE)
    case prefix
    from prefix(3) show ?case
    proof(induct rule: prefix_append_not_NilE[case_names incomplete1 continue1])
      case incomplete1 with pre continue(1) prefix(1,2) show ?case
        apply (clarsimp simp: spec.singleton.next_imp_le_conv)
        apply (drule spec[where x="trace.T s xs None"],
               drule mp[where P="trace.T s xs None  σf"])
         apply (force simp: spec.singleton.le_conv spec.map.singleton
                            le_inf_iff trace.less trace.split_All trace.less_eq_None
                 simp flip: spec.map_invmap.galois
                    intro!: spec.bind.incompleteI)+
        done
    next
      case (continue1 us)
      from continue(1,3) prefix(2) continue1(1,2)
           spec[OF pre, where x="trace.T (trace.init σf) (trace.rest σf) None"]
      have "trace.init σf, trace.rest σf, Some (vf v)  spec.pre P  spec.term.all A  f'  spec.post Q'"
        apply (cases σf)
        apply (clarsimp simp: spec.singleton.le_conv spec.singleton.next_imp_le_conv
                              trace.less le_inf_iff exI[where x=None]
                       split: option.split_asm
                       dest!: spec[where x=σf])
        apply (metis append_is_Nil_conv le_inf_iff pre trace.less_same_append_conv)
        done
      with pre continue(1,2,5) prefix(1,2) continue1
           spec[OF continue(4)[unfolded spec.singleton.next_imp_le_conv],
                where x="trace.T (trace.init σg) us None"]
      show ?case
        apply clarsimp
        apply (rule spec.bind.continueI[where v="vf v"], assumption)
        apply (clarsimp simp: spec.singleton.le_conv trace.split_All trace.less_eq_None trace.less)
        apply (metis append.assoc)
        done
    qed
  next
    case (maximal w')
    from maximal(1-3) continue(1,3)
         spec[OF pre, where x="trace.T (trace.init σf) (trace.rest σf) None"]
    have "trace.init σf, trace.rest σf, Some (vf v)  spec.pre P  spec.term.all A  f'  spec.post Q'"
      apply (cases σf)
      apply (clarsimp simp: spec.singleton.le_conv spec.singleton.next_imp_le_conv le_inf_iff
                     split: option.split_asm)
      apply (force simp: trace.less spec.singleton.mono trace.less_eq_same_append_conv
                   elim: notE order.trans[rotated]
                  dest!: spec[where x=σf] spec[where x=None])
      done
  with maximal(2-4) pre continue(2)
       spec[OF continue(4)[unfolded spec.singleton.next_imp_le_conv], where x="σg"]
  show ?case
    by (cases σg)
       (auto 0 2 intro!: spec.bind.continueI[where v="vf v"] exI[where x=s]
                  simp: spec.singleton.le_conv trace.split_All trace.less)
  qed
  then show ?case
    by (clarsimp simp: spec.singleton.next_imp_le_conv trace.split_all)
qed

lemmas bind = refinement.spec.bind_abstract[where vf=id, simplified spec.invmap.id, simplified]


subsubsection‹ Interference\label{sec:refinement-interference} ›

lemma rel_mono:
  assumes "r  r'"
  assumes "stable (snd ` (spec.steps A  r)) P"
  shows "spec.rel r  P⦄, A  spec.rel r', λ_::unit. P"
apply (subst (1) spec.rel.monomorphic_conv)
using assms(2)
proof(induct arbitrary: A rule: spec.kleene.star.fixp_induct[case_names adm bot step])
  case (step R A)
  have *: "spec.rel.act r  P⦄, spec.term.all A  spec.rel r', P"
    unfolding spec.rel.act_def
  proof(rule refinement.spec.action_rel)
    show "P s'"
      if "P s"
     and "(v, a, s, s')  {()} × (r  UNIV × Id)"
     and "(a, s, s')  spec.initial_steps (spec.term.all A)"
     for v a s s'
      using that monotoneD[OF stable.antimono_rel, unfolded le_bool_def, rule_format, OF _ step.prems,
                           where x="{(s, s')}"]
      by (cases "s = s'";
          force simp: spec.initial_steps.term.all stable.singleton_conv
                dest: subsetD[OF spec.initial_steps.steps_le])
    show "(a, s, s')  r'" if "(v, a, s, s')  {()} × (r  UNIV × Id)" and "s  s'" for v a s s'
      using that assms(1) by fast
  qed
  show ?case
    apply (rule refinement.sup[OF _ refinement.pre_g[OF refinement.spec.return spec.return.rel_le]])
    apply (subst spec.rel.unwind_bind)
    apply (rule refinement.spec.bind[OF step.hyps *])
    apply (force intro: monotoneD[OF stable.antimono_rel, unfolded le_bool_def, rule_format, OF _ step.prems]
                  dest: subsetD[OF spec.steps.refinement.spec.bind.res_le])
    done
qed simp_all

setup Sign.parent_path


subsubsection‹ Parallel\label{sec:refinement-parallel} ›

text‹

Our refinement rule for constspec.Parallel does not constrain the constituent processes in
any way, unlike Abadi and Plotkin's proposed rule (see \S\ref{sec:abadi_plotkin}).

›

setup Sign.mandatory_path "refinement.spec"

definition ―‹ roughly the constspec.Parallel construction with roles reversed ›
  env_hyp :: "('a  's pred)  (sequential, 's, unit) spec  'a set   ('a  (sequential, 's, unit) spec)  'a  (sequential, 's, unit) spec"
where
  "env_hyp P A as Ps a =
    spec.pre ( (P ` as))
       spec.amap (toConcurrent_fn (proc a))
          (spec.rel (({env}  proc ` as) × UNIV)
             (ias. spec.toConcurrent i (Ps i))
             spec.ainvmap toSequential_fn A)"

setup Sign.mandatory_path "env_hyp"

lemma mono:
  assumes "a. a  as  P a  P' a"
  assumes "A  A'"
  assumes "a. a  as  Ps a  Ps' a"
  shows "refinement.spec.env_hyp P A as Ps a  refinement.spec.env_hyp P' A' as Ps' a"
unfolding refinement.spec.env_hyp_def
apply (strengthen ord_to_strengthen(2)[OF assms(1)], assumption)
apply (strengthen ord_to_strengthen(1)[OF assms(2)])
apply (strengthen ord_to_strengthen(1)[OF assms(3)], assumption)
apply simp
done

lemma strengthen[strg]:
  assumes "a. a  as  st_ord F (P a) (P' a)"
  assumes "st_ord F A A'"
  assumes "a. a  as  st_ord F (Ps a) (Ps' a)"
  shows "st_ord F (refinement.spec.env_hyp P A as Ps a) (refinement.spec.env_hyp P' A' as Ps' a)"
using assms by (cases F; simp add: refinement.spec.env_hyp.mono)

setup Sign.parent_path

lemma Parallel:
  fixes A :: "(sequential, 's, unit) spec"
  fixes Q :: "'a  's  bool"
  fixes Ps :: "'a  (sequential, 's, unit) spec"
  fixes Ps' :: "'a  (sequential, 's, unit) spec"
  assumes "a. a  as  Ps a  P a⦄, refinement.spec.env_hyp P A as Ps' a  Ps' a, λv. Q a"
  shows "spec.Parallel as Ps  aas. P a⦄, A  spec.Parallel as Ps', λv. aas. Q a"
proof(cases "as = {}")
  case True then show ?thesis
    by (simp add: spec.Parallel.no_agents refinement.sort_of_refl[where G=] refinement.top)
next
  case False then show ?thesis
    unfolding refinement_def
    apply (subst (1) spec.Parallel_def)
    apply (simp only: spec.map_invmap.galois spec.invmap.inf
                      spec.next_imp.invmap spec.invmap.post spec.invmap.pre)
    apply (subst (1) spec.Parallel_def)
    apply (strengthen ord_to_strengthen(2)[OF spec.map_invmap.upper_lower_expansive])
    apply (subst inf.assoc)
    apply (subst spec.next_imp.infR)
    apply (simp only: spec.next_imp.contains inf.bounded_iff inf_le1)
    apply (strengthen ord_to_strengthen[OF assms], assumption)
    apply (simp only: spec.invmap.refinement id_apply simp_thms)
    apply (rule order.trans[rotated, OF spec.Abadi_Merz_Theorem4[where
               I=as
           and As="λa. spec.pre (P a)  spec.toConcurrent a (refinement.spec.env_hyp P A as Ps' a)"
           and Cs="λa. spec.toConcurrent a (Ps' a)  spec.post Q a"]])
    apply (simp only: inf.bounded_iff)
    apply (intro conjI)
      ―‹ the meat of constrefinement.spec.env_hyp
      apply (simp only: heyting)
      apply (rule INFI)
      apply (simp only: inf.bounded_iff
                  flip: INF_inf_distrib)
      apply (intro conjI)
       apply (force simp: ac_simps spec.pre.INF
                   intro: le_infI1 le_infI2)
      apply (simp add: refinement.spec.env_hyp_def ac_simps
                 flip: spec.map_invmap.galois)
      apply (rule conjI)
       apply (simp add: spec.map_invmap.galois spec.invmap.pre le_infI2
                   del: Inf_apply INF_apply; fail)
      apply (simp add: spec.map.mono le_infI2; fail)
     apply (simp add: spec.next_imp.contains heyting spec.post.INF flip: INF_inf_distrib; fail)
    apply (force simp: refinement_def)
    done
qed

setup Sign.parent_path


subsection‹ A relational assume/guarantee program logic for the ‹(sequential, 's, 'v) spec› lattice\label{sec:refinement-ag} ›

text‹

Here we develop an assume/guarantee story based on abstracting processes
(represented as safety properties) to binary relations.

Observations:
  this can be seen as a reconstruction of the algebraic account given by citet"vanStaden:2015" in our setting
  we show Heyting implication suffices for relations (see ag.refinement›)
    the processes' agent type is required to be typsequential
  we use predicates and not relations for pre/post assertions
    we can use the metalanguage to do some relational reasoning; see, for example, ag.name_pre_state›
  constId is the smallest significant assume and guarantee relation here; processes can always stutter any state

›

setup Sign.mandatory_path "ag"

abbreviation (input) assm :: "'s rel  (sequential, 's, 'v) spec" where
  "assm A  spec.rel ({env} × A  {self} × UNIV)"

abbreviation (input) guar :: "'s rel  (sequential, 's, 'v) spec" where
  "guar G  spec.rel ({env} × UNIV  {self} × G)"

setup Sign.parent_path

definition ag :: "'s pred  's rel  's rel  ('v  's pred)  (sequential, 's, 'v) spec" ("_⦄, _/  _, _" [0,0,0,0] 100) where
  "P⦄, A  G, Q = spec.pre P  ag.assm A H ag.guar G  spec.post Q"

setup Sign.mandatory_path "spec.invmap"

lemma ag: ―‹ Note af = id›
  fixes sf :: "'s  't"
  fixes vf :: "'v  'w"
  fixes A :: "'t rel"
  fixes G :: "'t rel"
  fixes P :: "'t pred"
  fixes Q :: "'w  't pred"
  shows "spec.invmap id sf vf (P⦄, A  G, Q) = λs. P (sf s)⦄, inv_image (A=) sf  inv_image (G=) sf, λv s. Q (vf v) (sf s)"
proof -
  have "{self} × UNIV  ({env} × inv_image A sf  UNIV × inv_image Id sf) = {self} × UNIV  {env} × inv_image (A=) sf"
   and "{env} × UNIV  ({self} × inv_image G sf  UNIV × inv_image Id sf) = {env} × UNIV  {self} × inv_image (G=) sf"
    by auto
  then show ?thesis
    by (simp add: ag_def spec.invmap.heyting spec.invmap.inf spec.invmap.rel spec.invmap.pre spec.invmap.post
                  ac_simps map_prod_vimage_Times Sigma_Un_distrib2
            flip:  inv_image_alt_def)
qed

setup Sign.parent_path

setup Sign.mandatory_path "ag"

lemma refinement:
  shows "P⦄, A  G, Q = P⦄, ag.assm A  ag.guar G, Q"
proof -
  have constrains_heyting_ag: "ag.assm A + ag.guar G = ag.assm A H ag.guar G"
    apply (rule antisym[OF spec.next_imp.heyting_le])
    apply (simp add: spec.next_imp.heyting heyting)
    apply (subst inf.commute)
    apply (rule spec.composition_principle_half[where a1="{self}" and a2="{env}"];
           force simp: spec.idle_le spec.term.closed.rel)
    done
  have constrains_heyting_post: "P + spec.post Q = P H spec.post Q"
    if "P  spec.term.closed _"
   for P :: "(sequential, _, _) spec"
    apply (rule antisym[OF spec.next_imp.heyting_le])
    apply (clarsimp simp: spec.next_imp.heyting)
    apply (metis spec.term.all.closed_conv[OF that] heyting.topL order_refl spec.term.all.post
                 spec.term.all_none spec.term.heyting_noneL_allR_mono spec.term.lower_upper_lower(2))
    done
  show ?thesis
    by (simp add: ag_def refinement_def
                  spec.pre.next_imp_eq_heyting spec.idle_le
                  constrains_heyting_ag spec.next_imp.infR spec.term.closed.rel
                  constrains_heyting_post[OF spec.term.closed.rel] heyting.infR heyting.curry_conv)
qed

lemma E:
  assumes "c  P⦄, A  G, Q"
  obtains "c  spec.pre P  ag.assm A H ag.guar G"
      and "c  spec.pre P  ag.assm A H spec.post Q"
using assms by (simp add: ag_def heyting.infR)

lemma pre_post_cong:
  assumes "P = P'"
  assumes "Q = Q'"
  shows "P⦄, A  G, Q = P'⦄, A  G, Q'"
using assms by simp

lemma pre_bot:
  shows "⦄, A  G, Q = "
    and "⦄, A  G, Q = "
    and "False⦄, A  G, Q = "
by (simp_all add: ag_def heyting.botL)

lemma post_top:
  shows "P⦄, A  UNIV,  = "
    and "P⦄, A  UNIV,  = "
    and "P⦄, A  UNIV, λ_ _. True = "
by (simp_all add: ag_def spec.rel.upper_top flip: Sigma_Un_distrib1)

lemma mono:
  assumes "P'  P"
  assumes "A'  A"
  assumes "G  G'"
  assumes "Q  Q'"
  shows "P⦄, A  G, Q  P'⦄, A'  G', Q'"
unfolding ag_def
apply (strengthen ord_to_strengthen(1)[OF assms(1)])
apply (strengthen ord_to_strengthen(1)[OF assms(2)])
apply (strengthen ord_to_strengthen(1)[OF assms(3)])
apply (strengthen ord_to_strengthen(1)[OF assms(4)])
apply simp
done

lemma strengthen[strg]:
  assumes "st_ord (¬ F) P P'"
  assumes "st_ord (¬ F) A A'"
  assumes "st_ord F G G'"
  assumes "st_ord F Q Q'"
  shows "st_ord F (P⦄, A  G, Q) (P'⦄, A'  G', Q')"
using assms by (cases F; simp add: ag.mono)

lemma strengthen_pre:
  assumes "st_ord (¬ F) P P'"
  shows "st_ord F (P⦄, A  G, Q) (P'⦄, A  G, Q)"
using assms by (cases F; simp add: ag.mono)

lemmas pre_ag = order.trans[OF _ ag.mono[OF order.refl _ _ order.refl], of c] for c
lemmas pre_a = ag.pre_ag[OF _ _ order.refl]
lemmas pre_g = ag.pre_ag[OF _ order.refl]

lemma pre:
  assumes "c  P⦄, A  G, Q"
  assumes "s. P' s  P s"
  assumes "A'  A"
  assumes "G  G'"
  assumes "v s. Q v s  Q' v s"
  shows "c  P'⦄, A'  G', Q'"
using assms(2-) by (blast intro: order.trans[OF assms(1) ag.mono])

lemmas pre_pre_post = ag.pre[OF _ _ order.refl order.refl, of c] for c

lemma pre_imp:
  assumes "s. P s  P' s"
  assumes "c  P'⦄, A  G, Q"
  shows "c  P⦄, A  G, Q"
using assms ag.pre by blast

lemmas pre_pre = ag.pre_imp[rotated]

lemma post_imp:
  assumes "v s . Q v s  Q' v s"
  assumes "c  P⦄, A  G, Q"
  shows "c  P⦄, A  G, Q'"
using assms ag.pre by blast

lemmas pre_post = ag.post_imp[rotated]
lemmas strengthen_post = ag.pre_post

lemmas reflcl_ag = spec.invmap.ag[where sf=id and vf=id, simplified spec.invmap.id, simplified]

lemma
  shows reflcl_a: "P⦄, A  G, Q = P⦄, A=  G, Q"
    and reflcl_g: "P⦄, A  G, Q = P⦄, A  G=, Q"
by (metis ag.reflcl_ag sup.left_idem sup_commute)+

lemma gen_asm_base:
  assumes "P  c  P'  P''⦄, A  G, Q"
  shows "c  P'  P  P''⦄, A  G, Q"
using assms by (simp add: ag_def spec.pre.conj spec.pre.K heyting.botL)

lemmas gen_asm =
  ag.gen_asm_base[where P'="True" and P''="True", simplified]
  ag.gen_asm_base[where P'="True", simplified]
  ag.gen_asm_base[where P''="True", simplified]
  ag.gen_asm_base

lemma post_conj:
  assumes "c  P⦄, A  G, Q"
  assumes "c  P⦄, A  G, Q'"
  shows "c  P⦄, A  G, λv. Q v  Q' v"
using assms by (simp add: ag_def spec.post.conj heyting)

lemma pre_disj:
  assumes "c  P⦄, A  G, Q"
  assumes "c  P'⦄, A  G, Q"
  shows "c  P  P'⦄, A  G, Q"
using assms by (simp add: ag_def spec.pre.disj inf_sup_distrib heyting)

lemma drop_imp:
  assumes "c  P⦄, A  G, Q"
  shows "c  P⦄, A  G, λv. Q' v  Q v"
using assms ag.strengthen_post by fastforce

lemma "prop":
  shows "c  P⦄, A  UNIV, λv. P"
by (simp add: ag.gen_asm(1) ag.post_top(3))

lemma name_pre_state:
  assumes "s. P s  c  (=) s⦄, A  G, Q"
  shows "c  P⦄, A  G, Q"
by (metis assms ag.refinement refinement.name_pre_state spec.idle.rel_le)

lemma conj_lift:
  assumes "c  P⦄, A  G, Q"
  assumes "c  P'⦄, A  G, Q'"
  shows "c  P  P'⦄, A  G, λv. Q v  Q' v"
using assms by (blast intro: ag.post_conj ag.pre)

lemma disj_lift:
  assumes "c  P⦄, A  G, Q"
  assumes "c  P'⦄, A  G, Q'"
  shows "c  P  P'⦄, A  G, λv. Q v  Q' v"
using assms by (simp add: ag_def spec.post.disj spec.pre.disj heyting inf_sup_distrib le_supI1 le_supI2)

lemma all_lift:
  assumes "x. c  P x⦄, A  G, Q x"
  shows "c  x. P x⦄, A  G, λv. x. Q x v"
using assms
by (auto simp: ag_def spec.pre.All spec.post.All le_Inf_iff heyting
    simp flip: INF_inf_const1 INF_inf_const2)

lemma interference_le:
  shows "spec.rel ({env} × UNIV)  P⦄, A  G, "
    and "spec.rel ({env} × UNIV)  P⦄, A  G, λ_. "
    and "spec.rel ({env} × UNIV)  P⦄, A  G, λ_ _. True"
by (auto simp: ag_def heyting spec.term.all.rel intro: spec.rel.mono inf.coboundedI1)

lemma assm_heyting:
  fixes Q :: "'v  's pred"
  shows "ag.assm r H P⦄, A  G, Q = P⦄, A  r  G, Q"
by (simp add: ag_def ac_simps Int_Un_distrib Times_Int_Times flip: heyting.curry_conv spec.rel.inf)

lemma augment_a: ―‹ instantiate A'›
  assumes "c  P⦄, A  G, Q"
  shows "c  P⦄, A  A'  G, Q"
by (blast intro: ag.pre_a[OF assms])

lemma augment_post: ―‹ instantiate Q›
  assumes "c  P⦄, A  G, λv. Q' v  Q v"
  shows "c  P⦄, A  G, Q'"
by (blast intro: ag.pre_post[OF assms])

lemma augment_post_imp: ―‹ instantiate Q›
  assumes "c  P⦄, A  G, λv. (Q v  Q' v)  Q v"
  shows "c  P⦄, A  G, Q'"
by (blast intro: ag.pre_post[OF assms])

setup Sign.parent_path

setup Sign.mandatory_path "spec.term.none"

lemma ag_le:
  shows "spec.term.none (P⦄, A  G, Q)  P⦄, A  G, "
by (simp add: ag.refinement spec.term.all.rel order.trans[OF spec.term.none.refinement_le])

setup Sign.parent_path

setup Sign.mandatory_path "ag.spec.term"

lemmas none_inteference =
  order.trans[OF spec.term.none.mono,
              OF ag.interference_le(1) ag.pre_post[where Q'=Q for Q, OF spec.term.none.ag_le, simplified]]

setup Sign.parent_path

setup Sign.mandatory_path "ag.spec"

lemma bind:
  assumes g: "v. g v  Q' v⦄, A  G, Q"
  assumes f: "f  P⦄, A  G, Q'"
  shows "f  g  P⦄, A  G, Q"
apply (subst ag.refinement)
apply (rule refinement.spec.bind[where f'="ag.guar G" and g'="ag.guar G", simplified spec.rel.wind_bind])
 apply (rule refinement.pre_a[OF g[unfolded ag.refinement]])
 apply (simp_all add: refinement.spec.bind.res.rel_le spec.term.all.rel f[unfolded ag.refinement])
done

lemma action:
  fixes F :: "('v × sequential × 's × 's) set"
  assumes Q: "v a s s'. P s; (v, a, s, s')  F  Q v s'"
  assumes G: "v s s'. P s; (v, self, s, s')  F; s  s'  (s, s')  G"
  shows "spec.action F  P⦄, A  G, Q"
proof -
  from G have *: "spec.pre P  spec.action F  spec.rel ({env} × UNIV  {self} × G)"
    by - (rule order.trans[OF spec.pre.inf_action_le(1) spec.action.rel_le]; auto)
  show ?thesis
    by (fastforce intro: order.trans[OF _ refinement.mono_stronger[OF order.refl _ _ order.refl]]
                         refinement.spec.action Q
                   simp: ag.refinement order.trans[OF *] spec.next_imp.closure.expansive spec.idle.rel_le)
qed

lemma return:
  shows "spec.return v  Q v⦄, A  G, Q"
by (auto simp: spec.return_def intro: ag.spec.action)

lemma Parallel_assm:
  shows "refinement.spec.env_hyp P (ag.assm A) as (ag.guar  G) a  ag.assm (A   (G ` (as - {a})))"
by (simp add: refinement.spec.env_hyp_def spec.invmap.rel flip: spec.rel.upper_INF spec.rel.inf)
   (force intro!: le_infI2 order.trans[OF spec.map.rel_le] spec.rel.mono_reflcl)

lemma Parallel_guar:
  shows "spec.Parallel as (ag.guar  G) = ag.guar (aas. G a)"
proof -
  have *: "{self} × Id  (insert env ((λx. self) ` as) × Id  map_prod toSequential_fn id ` (insert env (proc ` as) × UNIV  (xas. {proc x} × G x  (- {proc x}) × UNIV)))
         = {env} × UNIV  ({self} × Id  {self} ×  (G ` as))"
    by (rule antisym, force simp: toSequential_fn_def, (safe; force simp: map_prod_conv))
  show ?thesis
    apply (simp add: spec.Parallel_def spec.invmap.rel
               flip: spec.rel.INF spec.rel.inf)
    apply (subst spec.map.rel)
     apply (clarsimp; blast)
    apply (subst (1 2) spec.rel.reflcl[where A="{self}", symmetric])
    apply (clarsimp simp: ac_simps inf_sup_distrib image_Un image_image *
                          map_prod_image_Times map_prod_vimage_Times Times_Int_Times)
    done
qed

lemma Parallel:
  fixes A :: "'s rel"
  fixes G :: "'a  's rel"
  fixes Q :: "'a  's  bool"
  fixes Ps :: "'a  (sequential, 's, unit) spec"
  assumes proc_ag: "a. a  as  Ps a  P a⦄, A  (a'as-{a}. G a')  G a, λv. Q a"
  shows "spec.Parallel as Ps  aas. P a⦄, A  aas. G a, λrv. aas. Q a"
unfolding ag.refinement
apply (rule order.trans[OF _ refinement.mono[OF order.refl _ _ order.refl]])
  apply (rule refinement.spec.Parallel[where A="ag.assm A" and Ps'="ag.guar  G"])
  apply (rule order.trans[OF proc_ag, unfolded ag.refinement], assumption)
  apply (rule refinement.mono[OF order.refl _ _ order.refl])
   apply (force intro: ag.spec.Parallel_assm simp: ag.spec.Parallel_guar)+
done

setup Sign.parent_path


subsubsection‹ Stability rules ›

setup Sign.mandatory_path "spec"

lemma stable_pre_post:
  fixes S :: "('a, 's, 'v) spec"
  assumes "stable (snd ` r) P"
  assumes "spec.steps S  r"
  shows "S  spec.pre P H spec.post P"
proof -
  have "P (trace.final' s xs)"
    if "P s"
   and "trace.steps (trace.T s xs v)  r"
   for s :: 's and xs :: "('a × 's) list" and v :: "'v option"
    using that
  proof(induct xs arbitrary: s)
    case (Cons x xs) with stable (snd ` r) P show ?case
      by (cases x; cases "snd x = s";
          force simp: stable_def monotone_def dest: le_boolD)
  qed simp
  from this spec.steps S  r show ?thesis
    by - (rule spec.singleton_le_extI;
          auto dest: order.trans[where b=S]
               simp: spec.singleton.le_conv spec.singleton.heyting_le_conv trace.split_all spec.rel.galois
              split: option.split)
qed

lemma pre_post_stable:
  fixes P :: "'s  bool"
  assumes "stable (snd ` r) P"
  shows "spec.rel r  spec.pre P H spec.post P"
by (rule spec.stable_pre_post[OF assms spec.rel.lower_upper_contractive])

setup Sign.parent_path

setup Sign.mandatory_path "ag"

lemma stable_lift:
  assumes "stable (A  G) P'" ―‹ anything stable over A ∪ G› is invariant ›
  shows "P  P'⦄, A  G, λv. P'  Q v  P  P'⦄, A  G, λv. Q v  P'"
apply (simp add: ag_def spec.pre.conj heyting heyting.detachment le_infI2 flip: spec.heyting.post)
apply (simp add: ac_simps Sigma_Un_distrib2 Int_Un_distrib Times_Int_Times flip: spec.rel.inf)
apply (rule order.subgoal)
 apply (rule order.trans[OF _ spec.pre_post_stable[where r="{env} × A  {self} × G", simplified image_Un, simplified, OF assms]])
 apply (simp add: le_infI2; fail)
apply (simp add: ac_simps spec.post.conj)
apply (simp add: heyting.discharge le_infI1 flip: inf.assoc)
done

lemma stable_augment_base:
  assumes "c  P  P'⦄, A  G, λv. P'  Q v"
  assumes "stable (A  G) P'" ―‹ anything stable over A ∪ G› is invariant ›
  shows "c  P  P'⦄, A  G, λv. Q v  P'"
using order.trans[OF _ ag.stable_lift] assms by blast

lemma stable_augment:
  assumes "c  P'⦄, A  G, Q'"
  assumes "v s. P s; Q' v s  Q v s"
  assumes "stable (A  G) P"
  shows "c  P'  P⦄, A  G, Q"
apply (rule ag.strengthen_post)
 apply (rule ag.stable_augment_base[where Q=Q, OF _ assms(3)])
 apply (auto intro: assms(2) ag.pre[OF assms(1)])
done

lemma stable_augment_post:
  assumes "c  P'⦄, A  G, Q'" ―‹ resolve before application›
  assumes "v. stable (A  G) (Q' v  Q v)"
  shows "c  (v. Q' v  Q v)  P'⦄, A  G, Q"
apply (rule ag.pre_pre_post)
 apply (rule ag.stable_augment_base[where P=P' and Q=Q' and P'="(v. Q' v  Q v)"])
   apply (rule ag.pre_pre_post[OF assms(1)])
    using assms(2) apply (fast intro: stable.allI)+
done

lemma stable_augment_frame: ―‹ anything stable over A ∪ G› is invariant\label{sec:refinement-frame} ›
  assumes "c  P⦄, A  G, Q"
  assumes "stable (A  G) P'"
  shows "c  P  P'⦄, A  G, λv. Q v  P'"
using assms by (blast intro: ag.stable_augment[OF assms(1)])

setup Sign.parent_path

setup Sign.mandatory_path "ag.spec"

lemma stable_interference:
  assumes "stable (A  r) P"
  shows "spec.rel ({env} × r)  P⦄, A  G, P"
using assms
by (auto simp: ag_def ac_simps heyting Int_Un_distrib Times_Int_Times
    simp flip: spec.rel.inf
        intro: le_infI2 spec.rel.mono spec.pre_post_stable[simplified heyting ac_simps])

setup Sign.parent_path

setup Sign.mandatory_path "spec"

setup Sign.mandatory_path "cam"

lemma closed_ag:
  shows "P⦄, A  G, Q  spec.cam.closed ({env} × r)"
unfolding ag_def heyting.infR
by (blast intro: subsetD[OF spec.cam.closed.antimono, rotated])

setup Sign.parent_path

setup Sign.mandatory_path "interference"

lemma cl_ag_le:
  assumes P: "stable (A  r) P"
  assumes Q: "v. stable (A  r) (Q v)"
  shows "spec.interference.cl ({env} × r) (P⦄, A  G, Q)  P⦄, A  G, Q"
unfolding spec.interference.cl_def
by (rule ag.spec.bind ag.spec.return ag.spec.stable_interference spec.cam.least[OF _ spec.cam.closed_ag] assms order.refl)+

lemma closed_ag:
  assumes P: "stable (A  r) P"
  assumes Q: "v. stable (A  r) (Q v)"
  shows "P⦄, A  G, Q  spec.interference.closed ({env} × r)"
by (rule spec.interference.closed_clI[OF spec.interference.cl_ag_le[OF assms]])

setup Sign.parent_path

setup Sign.parent_path
(*<*)

end
(*>*)