(* * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) * * SPDX-License-Identifier: BSD-2-Clause *) (* A general calculus of refinement in Isabelle. *) chapter "Refinement" theory Simulation imports Main begin text \ A data type is a collection of three functions on three basic types. The three basic types are the private state space @{typ 'a}, the observable state space @{typ 'b} and the operations @{typ 'j}. The three functions are the initialisation @{term "Init"} which (potentially nondeterministically) produces a private state space from an observable one, the finalisation @{term Fin} which projects the observable part of the state space out of the private one, and finally @{term Step} that gives the (potentially nondeterministic) transition relation on the private state space for each operation in @{typ 'j}. In the simple case, the private state space is something like a tuple @{typ "'a \ 'b"} fully containing the observable part such that @{term Fin} just becomes the projection @{term snd}, and @{term "Init s"} constructs an additional private part, e.g. just @{term "{(f x, x)}"}. Hoare triples on the system and refinement are defined over the observable part of the state. \ record ('a,'b,'j) data_type = Init :: "'b \ 'a set" Fin :: "'a \ 'b" Step :: "'j \ ('a \ 'a) set" text \ A sequence of operations over a transition relation @{term \} is executed by applying the relation repeatedly. \ definition steps :: "('j \ ('a \ 'a) set) \ 'a set \ 'j list \ 'a set" where "steps \ \ foldl (\S j. \ j `` S)" text \ The sequence of operations in the data type is then executed in an initial state by initialising the private state, executing the transition relation over this private state, and finally projecting back out the set of final, observable states. \ definition execution :: "('a,'b,'j) data_type \ 'b \ 'j list \ 'b set" where "execution A s js \ Fin A ` steps (Step A) (Init A s) js" text \ A Hoare triple over a list of operations in the data type is the usual: given a state in the pre-condition, all resulting states of the execution must be in the post-condition: \ definition hoare_triple :: "('a,'b,'j) data_type \ 'b set \ 'j list \ 'b set \ bool" where "hoare_triple A P js Q \ \s \ P. execution A s js \ Q" text \ Refinement is defined by saying that all concrete behaviours are contained in their corresponding abstract ones. Only the private state spaces of the data type may differ. \ definition refines :: "('c,'b,'j) data_type \ ('a,'b,'j) data_type \ bool" (infix "\" 60) where "C \ A \ \js s. execution C s js \ execution A s js" text \ Alternatively, one may say that all Hoare triples proved on the abstract data type are true for the concrete one. \ lemma hoare_triple_refinement: "C \ A = (\P Q js. hoare_triple A P js Q \ hoare_triple C P js Q)" by (simp add: refines_def hoare_triple_def) blast \ \composing two relations\ definition rel_semi :: "('a \ 'b) set \ ('b \ 'c) set \ ('a \ 'c) set" (infixl ";;;" 65) where "A ;;; B \ A O B" text \ Refinement is a global property over all executions and/or all hoare triples. As this is hard to show, we define the weaker concept of forward simulation. \ definition fw_sim :: "('a \ 'c) set \ ('c,'b,'j) data_type \ ('a,'b,'j) data_type \ bool" where "fw_sim R C A \ (\s. Init C s \ R `` Init A s) \ (\j. R ;;; Step C j \ Step A j ;;; R) \ (\s s'. (s,s') \ R \ Fin C s' = Fin A s)" definition fw_simulates :: "('c,'b,'j) data_type \ ('a,'b,'j) data_type \ bool" (infixl "\\<^sub>F" 50) where "C \\<^sub>F A \ \R. fw_sim R C A" lemma fw_sim_steps: assumes steps: "t' \ steps (Step C) S' js" "S' \ R `` S" assumes sim: "fw_sim R C A" shows "\t \ steps (Step A) S js. (t,t') \ R" using steps proof (induct js arbitrary: S' S) case Nil thus ?case by (simp add: steps_def) blast next case (Cons j js) hence "t' \ steps (Step C) (Step C j `` S') js" by (clarsimp simp: steps_def) moreover { from Cons.prems have "S' \ R `` S" by simp moreover from sim have "R ;;; Step C j \ Step A j ;;; R" by (simp add: fw_sim_def) ultimately have "Step C j `` S' \ R `` (Step A j `` S)" by (simp add: rel_semi_def) blast } ultimately show ?case using Cons.hyps by (auto simp: steps_def) qed lemma sim_imp_refines: "C \\<^sub>F A \ C \ A" apply (clarsimp simp: refines_def execution_def fw_simulates_def) apply (rename_tac t) apply (drule fw_sim_steps) prefer 2 apply assumption apply (simp add: fw_sim_def) apply blast apply clarsimp apply (erule rev_image_eqI) apply (simp add: fw_sim_def) done text \ To further aid proofs, we define (private) invariants on data types. Private invariants are properties that are true throughout execution on the private state space of the state type. The purpose is to exploit these properties while showing forward simulation. \ definition invariant_holds :: "('a,'b,'j) data_type \ 'a set \ bool" (infix "\" 60) where "D \ I \ (\s. Init D s \ I) \ (\j. Step D j `` I \ I)" lemma invariant_T [iff]: "D \ UNIV" by (simp add: invariant_holds_def) lemma invariantI: "\ \s. Init D s \ I; \j. Step D j `` I \ I \ \ D \ I" by (simp add: invariant_holds_def) lemma invariant_CollectI [intro?]: assumes init: "\s a. s \ Init D a \ I s" assumes step: "\j s s'. \ I s; (s,s') \ Step D j \ \ I s'" shows "D \ Collect I" proof (rule invariantI) show "\a. Init D a \ Collect I" by (fast intro: init) next show "\j. Step D j `` Collect I \ Collect I" by (auto simp add: Image_def intro: step) qed lemma invariant_conjI: "D \ P \ D \ Q \ D \ P \ Q" by (simp add: invariant_holds_def) blast lemma invariant_conjI2: "\ D \ I; \s. Init D s \ I \ Init D s \ J; \j. Step D j `` (I \ J) \ J \ \ D \ I \ J" by (simp add: invariant_holds_def) blast text \ We can now define forward simulation with an invariant. The proof obligation for the step and final case in the correspondence proof can now assume that the invariant holds. The invariant itself can be shown separately. \ definition LI :: "('a,'b,'j) data_type \ ('c,'b,'j) data_type \ ('a \ 'c) set \ ('a \ 'c) set \ bool" where "LI A C R I \ (\s. Init C s \ R `` Init A s) \ (\j. (R \ I) ;;; Step C j \ Step A j ;;; R) \ (\s s'. (s,s') \ R \ I \ Fin C s' = Fin A s)" lemma LI_fw_sim: assumes ia: "A \ I\<^sub>a" and ic: "C \ I\<^sub>c" and li: "LI A C r (I\<^sub>a \ I\<^sub>c)" shows "fw_sim (r \ I\<^sub>a \ I\<^sub>c) C A" proof - from li have init: "\s. Init C s \ r `` Init A s" and step: "\j. (r \ (I\<^sub>a \ I\<^sub>c)) ;;; Step C j \ Step A j ;;; r" and fin: "(\s s'. (s,s') \ r \ (I\<^sub>a \ I\<^sub>c) \ Fin C s' = Fin A s)" by (auto simp: LI_def) from ia have "\s. (r \ (UNIV \ I\<^sub>c) ) `` Init A s = (r \ (I\<^sub>a \ I\<^sub>c)) `` Init A s" by (simp add: invariant_holds_def, blast) moreover from init ic have "\s. Init C s \ (r \ (UNIV \ I\<^sub>c)) `` Init A s" by (simp add: invariant_holds_def, blast) ultimately have initI: "\s. Init C s \ (r \ (I\<^sub>a \ I\<^sub>c)) `` Init A s" by simp moreover { fix j from step have "r \ (I\<^sub>a \ I\<^sub>c) ;;; Step C j \ Step A j ;;; r".. also have "r \ (I\<^sub>a \ I\<^sub>c) = ((UNIV \ I\<^sub>a) \ Id) ;;; r ;;; ((I\<^sub>c \ UNIV) \ Id)" (is "_ = ?I\<^sub>a ;;; r ;;; ?I\<^sub>c") by (simp add: rel_semi_def, blast) finally have "?I\<^sub>a ;;; r ;;; ?I\<^sub>c ;;; Step C j \ ?I\<^sub>a ;;; Step A j ;;; r" by (simp add: rel_semi_def, blast) also from ia have "\ \ Step A j ;;; ?I\<^sub>a ;;; r" by (simp add: invariant_holds_def rel_semi_def, blast) finally have "?I\<^sub>a ;;; r ;;; ?I\<^sub>c ;;; Step C j;;; ?I\<^sub>c \ Step A j ;;; ?I\<^sub>a ;;; r ;;; ?I\<^sub>c" by (simp add: rel_semi_def, blast) also from ic have "?I\<^sub>a ;;; r ;;; ?I\<^sub>c ;;; Step C j;;; ?I\<^sub>c = ?I\<^sub>a ;;; r ;;; ?I\<^sub>c ;;; Step C j" by (simp add: invariant_holds_def rel_semi_def, blast) finally have "r \ (I\<^sub>a \ I\<^sub>c) ;;; Step C j \ Step A j ;;; r \ (I\<^sub>a \ I\<^sub>c)" by (simp add: rel_semi_def, blast) } ultimately show "fw_sim (r \ I\<^sub>a \ I\<^sub>c) C A" using fin by (simp add: fw_sim_def) qed lemma weaken_LI: assumes LI: "LI A C R I'" and weaker: "I \ I'" shows "LI A C R I" proof - from weaker have step: "\j. R \ I ;;; Step C j \ R \ I' ;;; Step C j" by (fastforce simp: rel_semi_def relcomp_def) from weaker have fin: "\S. S \ R \ I \ S \ R \ I'" by fastforce from subset_trans[OF step] fin LI show ?thesis by (clarsimp simp: LI_def) qed lemma fw_sim_eq_LI: "fw_sim r C A = LI A C r UNIV" by (simp add: fw_sim_def LI_def) lemma fw_sim_LI: "fw_sim r C A \ LI A C r I" by (simp add: fw_sim_eq_LI weaken_LI[where I'=UNIV]) lemma L_invariantI: assumes "A \ I\<^sub>a" and "C \ I\<^sub>c" and "LI A C r (I\<^sub>a \ I\<^sub>c)" shows "C \\<^sub>F A" using assms by (simp add: fw_simulates_def, rule_tac x="r \ I\<^sub>a \ I\<^sub>c" in exI, simp add: LI_fw_sim) lemma refinement_refl[simp]: "A \ A" by (simp add: refines_def) lemma refinement_trans [trans]: "\C \ B; B \ A\ \ C \ A" by (simp add: refines_def) blast lemma fw_inv_transport: "\ A \ I\<^sub>a; C \ I\<^sub>c; LI A C R (I\<^sub>a \ I\<^sub>c) \ \ C \ {s'. \s. (s,s') \ R \ s \ I\<^sub>a \ s' \ I\<^sub>c}" apply (clarsimp simp: LI_def invariant_holds_def) apply (rule conjI) apply (rule allI) apply clarsimp apply (subgoal_tac "x \ (R `` Init A s)") prefer 2 apply fastforce apply blast apply clarsimp apply (erule_tac x=j in allE)+ apply (clarsimp simp: rel_semi_def) apply (subgoal_tac "(s,x) \ Step A j O R") prefer 2 apply blast apply blast done lemma fw_sim_refl: "fw_sim Id A A" apply (simp add: fw_sim_def rel_semi_def) done lemma fw_simulates_refl[simp]: "A \\<^sub>F A" apply (simp add: fw_simulates_def fw_sim_refl exI[where x="Id"]) done lemma fw_sim_trans: "\fw_sim Q C B; fw_sim R B A\ \ fw_sim (R O Q) C A" by (auto simp: fw_sim_def rel_semi_def; blast) lemma fw_simulates_trans: "\C \\<^sub>F B; B \\<^sub>F A\ \ C \\<^sub>F A" apply (auto simp: fw_simulates_def dest: fw_sim_trans) done end