Theory WickersonDoddsParkinson

(*<*)
theory WickersonDoddsParkinson
imports
  "Assume_Guarantee"
begin

(*>*)
section‹ Wickerson, Dodds and Parkinson: explicit stabilisation\label{sec:explicit_stabilisation} ›

text‹

Notes on citet‹"WickersonDoddsParkinson:2010"› (all references here are to the technical report):
 ▪ motivation: techniques for eliding redundant stability conditions
  ▪ the standard rules check the interstitial assertion in ‹c ; d› twice
 ▪ they claim in \S7 to supersede the ``mid stability'' of citet‹‹\S4.1› in "Vafeiadis:2008"› (wssa, sswa)
 ▪ Appendix D:
  ▪ not a complete set of rules
  ▪ \textsc{AtomR-S} does not self-compose: consider ‹c ; d› -- the interstitial assertion is either a floor or ceiling
   ▪ every step therefore requires a use of weakening/monotonicity

The basis of their approach is to make assertions a function of a
relation (a ∗‹rely›). By considering a set of
relations, a single rely-guarantee specification can satisfy several
call sites. Separately they tweak the RGSep rules of citet‹"Vafeiadis:2008"›.

The definitions are formally motivated as follows (\S3):

\begin{quote}

Our operators can also be defined using Dijkstra's predicate
transformer semantics: ‹⌊p⌋R› is the
weakest precondition of ‹R*› given postcondition
‹p›, while ‹⌈p⌉R› is the
strongest postcondition of ‹R*› given
pre-condition ‹p›.

\end{quote}

The following adapts their definitions and proofs to our setting.

›

setup ‹Sign.mandatory_path "wdp"›

definition floor :: "'a rel ⇒ 'a pred ⇒ 'a pred" where ―‹ An interior operator, or a closure in the dual lattice ›
  "floor r P s ⟷ (∀s'. (s, s') ∈ r* ⟶ P s')"

definition ceiling :: "'a rel ⇒ 'a pred ⇒ 'a pred" where ―‹ A closure operator ›
  "ceiling r P s ⟷ (∃s'. (s', s) ∈ r* ∧ P s')"

setup ‹Sign.mandatory_path "floor"›

lemma empty_rel[simp]:
  shows "wdp.floor {} P = P"
by (simp add: wdp.floor_def fun_eq_iff)

lemma reflcl:
  shows "wdp.floor (r=) = wdp.floor r"
by (simp add: wdp.floor_def fun_eq_iff)

lemma const:
  shows "wdp.floor r ⟨c⟩ = ⟨c⟩"
by (auto simp: wdp.floor_def fun_eq_iff)

lemma contractive:
  shows "wdp.floor r P ≤ P"
by (simp add: wdp.floor_def le_fun_def le_bool_def)

lemma idempotent:
  shows "wdp.floor r (wdp.floor r P) = wdp.floor r P"
by (auto simp: fun_eq_iff wdp.floor_def dest: rtrancl_trans)

lemma mono:
  assumes "r' ⊆ r"
  assumes "P ≤ P'"
  shows "wdp.floor r P ≤ wdp.floor r' P'"
using assms by (auto 6 0 simp add: wdp.floor_def le_bool_def le_fun_def dest: rtrancl_mono)

lemma strengthen[strg]:
  assumes "st_ord (¬F) r r'"
  assumes "st_ord F P P'"
  shows "st_ord F (wdp.floor r P) (wdp.floor r' P')"
using assms by (cases F; simp add: wdp.floor.mono)

lemma weakest:
  assumes "Q ≤ P"
  assumes "stable r Q"
  shows "Q ≤ wdp.floor r P"
using assms by (simp add: wdp.floor_def stable_def monotone_def le_fun_def le_bool_def) (metis rtrancl_induct)

lemma Chernoff:
  assumes "P ≤ Q"
  shows "(wdp.floor r P ∧ Q) ≤ wdp.floor r Q"
using assms by (simp add: wdp.floor_def le_fun_def le_bool_def)

lemma floor1:
  assumes "r ⊆ r'"
  shows "wdp.floor r' (wdp.floor r P) = wdp.floor r' P"
unfolding wdp.floor_def by (meson assms rtrancl_cl.cl_mono rtrancl_eq_or_trancl rtrancl_trans)

lemma floor2:
  assumes "r ⊆ r'"
  shows "wdp.floor r (wdp.floor r' P) = wdp.floor r' P"
by (metis assms antisym wdp.floor.contractive wdp.floor.idempotent wdp.floor.mono)

setup ‹Sign.parent_path›

interpretation ceiling: closure_complete_lattice_distributive_class "wdp.ceiling r" for r
by standard (auto 5 0 simp: wdp.ceiling_def le_fun_def le_bool_def dest: rtrancl_trans)

setup ‹Sign.mandatory_path "ceiling"›

lemma empty_rel[simp]:
  shows "wdp.ceiling {} P = P"
by (simp add: wdp.ceiling_def fun_eq_iff)

lemma reflcl:
  shows "wdp.ceiling (r=) = wdp.ceiling r"
by (simp add: wdp.ceiling_def fun_eq_iff)

lemma const:
  shows "wdp.ceiling r ⟨c⟩ = ⟨c⟩"
by (auto simp: wdp.ceiling_def fun_eq_iff)

lemma mono:
  assumes "r ⊆ r'"
  assumes "P ≤ P'"
  shows "wdp.ceiling r P ≤ wdp.ceiling r' P'"
using assms by (auto 7 0 simp: wdp.ceiling_def le_bool_def le_fun_def dest: rtrancl_mono)

lemma strengthen[strg]:
  assumes "st_ord F r r'"
  assumes "st_ord F P P'"
  shows "st_ord F (wdp.ceiling r P) (wdp.ceiling r' P')"
using assms by (cases F; simp add: wdp.ceiling.mono)

lemma strongest:
  assumes "P ≤ Q"
  assumes "stable r Q"
  shows "wdp.ceiling r P ≤ Q"
using assms by (simp add: wdp.ceiling_def stable_def monotone_def le_fun_def le_bool_def) (metis rtrancl_induct)

lemma ceiling1:
  assumes "r ⊆ r'"
  shows "wdp.ceiling r' (wdp.ceiling r P) = wdp.ceiling r' P"
unfolding wdp.ceiling_def by (meson assms rtrancl_cl.cl_mono rtrancl_eq_or_trancl rtrancl_trans)

lemma ceiling2:
  assumes "r ⊆ r'"
  shows "wdp.ceiling r (wdp.ceiling r' P) = wdp.ceiling r' P"
by (metis assms antisym wdp.ceiling.ceiling1 wdp.ceiling.expansive wdp.ceiling.idempotent(1))

setup ‹Sign.parent_path›

setup ‹Sign.mandatory_path "stable"›

lemma floor:
  shows "stable r (wdp.floor r P)"
unfolding wdp.floor_def stable_def monotone_def by (simp add: converse_rtrancl_into_rtrancl le_boolI)

lemma ceiling:
  shows "stable r (wdp.ceiling r P)"
by (fastforce simp: wdp.ceiling_def stable_def monotone_def le_bool_def
              dest: rtrancl_into_rtrancl)

lemma floor_conv:
  assumes "stable r P"
  shows "P = wdp.floor r P"
using assms unfolding wdp.floor_def stable_def monotone_def le_bool_def fun_eq_iff
by (metis rtrancl_refl rtrancl_induct)

lemma ceiling_conv:
  assumes "stable r P"
  shows "P = wdp.ceiling r P"
using assms unfolding wdp.ceiling_def stable_def monotone_def le_bool_def fun_eq_iff
by (metis rtrancl_refl rtrancl_induct)

setup ‹Sign.parent_path›

lemma floor_alt_def: ―‹ citet‹‹\S3› in "WickersonDoddsParkinson:2010"› ›
  shows "wdp.floor r P = ⨆{Q. Q ≤ P ∧ stable r Q}"
by (rule antisym)
   (auto simp: Sup_upper wdp.floor.contractive wdp.stable.floor
        intro: wdp.floor.weakest[unfolded le_bool_def le_fun_def, rule_format])

lemma ceiling_alt_def: ―‹ citet‹‹\S3› in "WickersonDoddsParkinson:2010"› ›
  shows "wdp.ceiling r P = ⨅{Q. P ≤ Q ∧ stable r Q}"
by (rule antisym)
   (auto simp: Inf_lower wdp.ceiling.expansive wdp.stable.ceiling
         dest: wdp.ceiling.strongest[unfolded le_bool_def le_fun_def, rule_format, rotated])

lemma duality_floor_ceiling:
  shows "wdp.ceiling r (¬P) = (¬wdp.floor (r¯) P)"
by (simp add: wdp.ceiling_def wdp.floor_def fun_eq_iff rtrancl_converse)

lemma ceiling_floor:
  assumes "r ⊆ r'"
  shows "wdp.ceiling r (wdp.floor r' P) = wdp.floor r' P"
by (metis assms wdp.ceiling.ceiling2 wdp.stable.ceiling_conv wdp.stable.floor)

lemma floor_ceiling:
  assumes "r ⊆ r'"
  shows "wdp.floor r (wdp.ceiling r' P) = wdp.ceiling r' P"
by (metis assms wdp.floor.floor2 wdp.stable.ceiling wdp.stable.floor_conv)

lemma floor_ceilr:
  shows "wdp.floor (ceilr P) P = P"
by (metis ceilr.stable wdp.stable.floor_conv)

lemma ceiling_ceilr:
  shows "wdp.ceiling (ceilr P) P = P"
by (metis ceilr.stable wdp.stable.ceiling_conv)

setup ‹Sign.parent_path›


subsection‹ Assume/Guarantee rules\label{sec:explicit_stabilisation-ag} ›

paragraph‹ \S3.2 traditional assume/guarantee rules ›

setup ‹Sign.mandatory_path "wdp"›

lemma action: ―‹ arbitrary ‹A› ›
  fixes F :: "('v × 's × 's) set"
  assumes Q: "⋀v s s'. ⟦P s; (v, s, s') ∈ F⟧ ⟹ Q v s'"
  assumes G: "⋀v s s'. ⟦P s; s ≠ s'; (v, s, s') ∈ F⟧ ⟹ (s, s') ∈ G"
  shows "prog.p2s (prog.action F) ≤ ⦃wdp.floor A P⦄, A ⊢ G, ⦃λv. wdp.ceiling A (Q v)⦄"
by (rule ag.prog.action)
   (auto simp: wdp.stable.floor wdp.stable.ceiling
        intro: G
         dest: Q[rotated]
         elim: wdp.floor.contractive[unfolded le_fun_def le_bool_def, rule_format]
               wdp.ceiling.expansive[unfolded le_fun_def le_bool_def, rule_format])

lemmas mono = ag.mono
lemmas bind = ag.prog.bind

text‹ etc. -- the other rules are stock ›

setup ‹Sign.parent_path›


paragraph‹ \S4, Appendix C parametric specifications ›

definition pag :: "('s rel ⇒ 's pred) ⇒ 's rel set ⇒ 's rel ⇒ ('s rel ⇒ 'v ⇒ 's pred) ⇒ (sequential, 's, 'v) spec" (‹⦃_⦄, _/ ⊢P _, ⦃_⦄› [0,0,0,0] 100) where
  "⦃P⦄, As ⊢P G, ⦃Q⦄ = (⨅A∈As. ⦃P A⦄, A ⊢ G, ⦃Q A⦄)"

setup ‹Sign.mandatory_path "pag"›

lemma empty:
  shows "⦃P⦄, {} ⊢P G, ⦃Q⦄ = ⊤"
by (simp add: pag_def)

lemma singleton:
  shows "⦃P⦄, {A} ⊢P G, ⦃Q⦄ = ⦃P A⦄, A ⊢ G, ⦃Q A⦄"
by (simp add: pag_def)

lemma mono: ―‹ strengthening of the ▩‹WEAKEN› rule in Figure~4, needed for the example  ›
  assumes "⋀A. A ∈ As' ⟹ P' A ≤ P A"
  assumes "As' ≤ As"
  assumes "G ≤ G'"
  assumes "⋀A. A ∈ As' ⟹ Q A ≤ Q' A"
  shows "⦃P⦄, As ⊢P G, ⦃Q⦄ ≤ ⦃P'⦄, As' ⊢P G', ⦃Q'⦄"
by (simp add: assms pag_def INF_superset_mono[OF ‹As' ≤ As› ag.mono] le_funD)

lemma action: ―‹ allow assertions to depend on assume ‹A›, needed for the example ›
  fixes F :: "('v × 's × 's) set"
  assumes Q: "⋀A v s s'. ⟦A ∈ As; P A s; (v, s, s') ∈ F⟧ ⟹ Q A v s'"
  assumes G: "⋀A v s s'. ⟦A ∈ As; P A s; s ≠ s'; (v, s, s') ∈ F⟧ ⟹ (s, s') ∈ G"
  shows "prog.p2s (prog.action F) ≤ ⦃λA. wdp.floor A (P A)⦄, As ⊢P G, ⦃λA v. wdp.ceiling A (Q A v)⦄"
by (simp add: assms pag_def wdp.action INFI)

lemmas sup = ag.prog.sup

lemma bind:
  assumes "⋀v. prog.p2s (g v) ≤ ⦃λA. Q' A v⦄, As ⊢P G, ⦃Q⦄"
  assumes "prog.p2s f ≤ ⦃P⦄, As ⊢P G, ⦃Q'⦄"
  shows "prog.p2s (f ⤜ g) ≤ ⦃P⦄, As ⊢P G, ⦃Q⦄"
unfolding pag_def
by (fastforce intro: INFI ag.prog.bind[rotated]
                     order.trans[OF assms(2) pag.mono[OF _ _ _ order.refl]]
                     order.trans[OF assms(1) pag.mono[where As'="{A}" for A], simplified pag.singleton]
          simp flip: pag.singleton)+

lemma parallel:
  assumes "prog.p2s c1 ≤ ⦃P1⦄, (∪) G2 ` A ⊢P G1, ⦃Q1⦄"
  assumes "prog.p2s c2 ≤ ⦃P2⦄, (∪) G1 ` A ⊢P G2, ⦃Q2⦄"
  shows "prog.p2s (prog.parallel c1 c2)
      ≤ ⦃λR. P1 (R ∪ G2) ∧ P2 (R ∪ G1)⦄, A ⊢P G1 ∪ G2, ⦃λR v. Q1 (R ∪ G2) v ∧ Q2 (R ∪ G1) v⦄"
unfolding pag_def
by (force intro: INFI ag.prog.parallel order.trans[OF assms(1) pag.mono]
                 order.trans[OF assms(2) pag.mono]
      simp flip: pag.singleton)

text‹ etc. -- the other rules follow similarly ›

setup ‹Sign.parent_path›


subsection‹ Examples\label{sec:explicit_stabilisation_eg} ›

text‹

There is not always a single (traditional) most general assume/guarantee specification (\S2.1).

›

type_synonym state = int ―‹ just ‹x› ›
abbreviation (input) "incr ≡ prog.write ((+) 1)" ―‹ atomic increment ›
abbreviation (input) increases :: "int rel" where "increases ≡ {(x, x'). x ≤ x'}"

lemma ag_incr1: ―‹ the precondition is stable as the rely is very strong ›
  shows "prog.p2s incr ≤ ⦃(=) c⦄, {} ⊢ increases, ⦃⟨(=) (c + 1)⟩⦄"
by (rule ag.prog.action; simp add: stable.empty)

lemma ag_incr2: ―‹ note the weaker precondition due to the larger assume ›
  shows "prog.p2s incr ≤ ⦃(≤) c⦄, increases ⊢ increases, ⦃⟨(≤) (c + 1)⟩⦄"
by (rule ag.prog.action; auto simp: stable_def monotone_def le_bool_def)

lemma ag_incr1_par_incr1:
  shows "prog.p2s (incr ∥ incr) ≤ ⦃λx. c ≤ x⦄, increases ⊢ increases, ⦃λ_ x. c + 1 ≤ x⦄"
apply (rule ag.pre_pre)
 apply (rule ag.pre_post)
  apply (rule ag.prog.parallel[where P1="λx. c ≤ x" and P2="λx. c ≤ x"
                                 and Q1="λ_ x. c + 1 ≤ x" and Q2="λ_ x. c + 1 ≤ x"
                                 and G1="increases" and G2="increases", simplified])
    apply (rule ag.prog.action; simp add: stable_def monotone_def le_boolI; fail)
   apply (rule ag.prog.action; simp add: stable_def monotone_def le_boolI; fail)
  apply simp_all
done

text‹

Using explicit stabilisation we can squash the two specifications for const‹incr› into a single one (\S4).

›

lemma ―‹ postcondition cannot be simplified for arbitrary ‹A› ›
  shows "prog.p2s incr ≤ ⦃wdp.ceiling A ((=) c)⦄, A ⊢ increases, ⦃⟨wdp.ceiling A (λs. wdp.ceiling A ((=) c) (s - 1))⟩⦄"
by (rule ag.pre_pre[OF wdp.action]) (simp add: wdp.floor_ceiling)+

―‹ The set of assumes that commute with the increment ›
abbreviation (input) comm_xpp :: "int rel set" where
  "comm_xpp ≡ {A. ∀p s. wdp.ceiling A p (s - 1) = wdp.ceiling A (λs. p (s - 1)) s}"

lemma pag_incr: ―‹ postcondition can be simplified wrt ‹comm_xpp› ›
  shows "prog.p2s incr ≤ ⦃λA. wdp.ceiling A ((=) c)⦄, comm_xpp ⊢P increases, ⦃λA. ⟨wdp.ceiling A ((=) (c + 1))⟩⦄"
apply (rule order.trans[OF _ pag.mono[OF _  order.refl order.refl]])
  apply (rule pag.action[where P="λA. wdp.ceiling A ((=) c)"
                           and Q="λA v s. wdp.ceiling A ((=) c) (s - 1)"])
   apply (simp_all add: wdp.floor_ceiling eq_diff_eq)
done

―‹ the two earlier specifications can be recovered ›
lemma
  shows "prog.p2s incr ≤ ⦃(=) c⦄, {} ⊢ increases, ⦃⟨(=) (c + 1)⟩⦄"
apply (rule order.trans[OF pag_incr])
apply (subst pag.singleton[symmetric])
apply (rule pag.mono; force)
done

lemma
  shows "prog.p2s incr ≤ ⦃(≤) c⦄, increases ⊢ increases, ⦃⟨(≤) (c + 1)⟩⦄"
apply (rule order.trans[OF pag_incr[where c=c]])
apply (subst pag.singleton[symmetric])
apply (rule pag.mono; force simp: wdp.ceiling_def order_rtrancl dest: zless_imp_add1_zle)
done

(*<*)

end
(*>*)