theory week10B_demo imports Main "autocorres-2015/lib/wp/NonDetMonadVCG" "~~/src/HOL/Eisbach/Eisbach_Tools" begin lemmas dup = thin_rl[of "PROP W" "PROP W" for W] section \; combinator\ lemma assumes B: "C" shows "(A \ C) \ (B \ C) \ C" apply (intro conjI ; (rule impI, rule B | rule B)) done section Eisbach_Demo lemma "(A \ B) \ (A \ C) \ (B \ C) \ C" apply (rule impI) apply (erule conjE)+ apply (erule disjE) apply (erule impE) apply assumption apply assumption apply (erule impE,assumption) apply assumption done lemma "(A \ B) \ (A \ C) \ (B \ C) \ C" apply (assumption | rule conjI impI | erule conjE impCE disjE | (erule notE,assumption))+ done method my_awesome_method = (assumption | rule conjI impI | erule conjE impCE disjE | (erule notE,assumption))+ lemma "(A \ B) \ (A \ C) \ (B \ C) \ C" by my_awesome_method subsection \Fact Abstraction\ text \The uses keyword is a fact parameter that must always be provided at run-time\ method my_awesome_method2 uses intros elims = (assumption | rule intros | erule elims | (erule notE,assumption))+ lemma "(A \ B) \ (A \ C) \ (B \ C) \ C" by (my_awesome_method2 intros: conjI impI elims: conjE impCE disjE) text \Named theorems are facts that can grow and shrink using attributes\ named_theorems intros and elims lemmas [intros] = conjI impI lemmas [elims] = conjE impCE disjE thm intros thm elims declare FalseE[intros] thm intros text \Not a safe rule. Lets get rid of it.\ declare FalseE[intros del] method my_awesome_method3 declares intros elims = (assumption | rule intros | erule elims | (erule notE,assumption))+ lemma "(A \ B) \ (A \ C) \ (B \ C) \ C" by my_awesome_method3 lemma "B \ (B \ C) \ C" by (my_awesome_method3 elims: iffE) method prop_solver' declares intros elims = ((assumption | rule intros | erule elims | (erule notE, (prop_solver' ; fail)))+)[1] lemmas [intros] = conjI impI disjCI iffI notI lemmas [elims] = impCE conjE disjE lemma "(A \ B) \ (A \ C) \ (B \ C) \ C" apply prop_solver' done text \The "; fail" suffix is necessary to ensure we don't terminate in an unsolvable state. This is the same as using the "solves" combinator (i.e. solves \prop_solver'\)\ lemma "\ A \ \(A \ B) \ False" apply prop_solver' done subsection \Term Abstraction\ text \Methods are implicitly polymorphic over term parameters\ method elim_all for Q :: "'a \ bool" and y :: 'a = (erule allE[where P = Q and x = y ]) lemma "\x. P x \ P y" apply (elim_all P y) apply assumption done text \Term parameters are type-checked\ method induct_auto for l :: "'a list" = (induct l; auto) lemma "distinct l \ card (set l) = length l" by (induct_auto l) lemma "length (l @ l') = length l' + length l" (*by (induct_auto "length l")*) sorry subsection \Method abstraction\ text \Method expressions can be given as parameters, to create higher-order methods\ method induct_then for x :: 'a methods m = (induct x; m) lemma "distinct l \ card (set l) = length l" by (induct_then l \simp\) subsection \Interlude: Helper methods\ text \Eisbach provides a few helper methods to aid in run-time debugging.\ lemma "True" apply (print_fact conjE) apply (print_term "True \ False") by (rule TrueI) text \These get run every time a method backtracks, which is useful to gain insight into its behaviour\ lemma "\x :: 'a. Q x \ \x :: 'a. P x \ P y" by (erule allE[where x=y], print_term P, assumption) text \Here we see that the first call to "assumption" fails, so backtracking forces "erule allE" to specialize a different quantifier. The method "print_term" is then called again.\ subsection \The match method\ text \The match method is a proof method specifically written for use in Eisbach. It performs structured decomposition of the goal known as "subgoal focusing". This allows subgoal elements to be named, based on patterns.\ text \When given exactly the right assumptions, the "simplified" method works. However, the additional assumption "ceqb" will cause it to loop.\ lemma assumes Pb: "P b" and beqa: "b = a" and aeqc: "a = c" and ceqb: "c = a" shows "P c" by (rule Pb[simplified beqa aeqc]) text \Additionally, if we add all of the assumptions to the subgoal, the simplifier will simply loop. Proof methods "metis" and "blast" will still solve the goal, however.\ lemma assumes Pb : "P b" shows "\a c. b = a \ a = c \ c = a \ P c" (*apply (simp add: Pb)?*) (*apply (rule subst) apply assumption*) by (rule subst, assumption, rule subst, assumption, rule Pb) lemma assumes Pb : "P b" shows "\a c. b = a \ a = c \ c = a \ P c" apply (match premises in H:_ \ \print_fact H\) back back apply (match premises in H:_ (multi) \ \print_fact H\) apply (match premises in H:"b = x" for x \ \print_fact H, print_term x\) apply (match premises in H:"x = y" for x y \ \print_fact H, print_term x, print_term y\) back (* apply (match premises in H:_ (multi) \ \rule Pb[simplified H]\) *) apply (rule dup) apply (match conclusion in "P y" for y \ \match premises in H: "x = y" and H': "b = x" for x \ \rule Pb[simplified H H']\\) apply (rule dup) apply (match premises in H: "_ = x" and H': "x = _" for x \ \rule Pb[simplified H H']\) apply (rule dup) apply (match premises in H[simp]: "_ = x" and H'[simp]: "x = _" for x \ \rule Pb[simplified]\) apply (match conclusion in "P y" for y \ \match premises in H[thin]: "y = _" \ \succeed\\) apply (match premises in H:_ (multi) \ \rule Pb[simplified H]\) done text \Multiple match patterns can be given at once and they will be interpreted top-down. However, once a pattern is matched backtracking will not occur past it.\ thm conjE lemma "Q \ Q' \ Q" apply (match premises in (* H:"Q \ _" \ \rule H\ \*) H:"_ \ _" \ \rule conjE[OF H]\) by simp+ subsection \Putting it all together\ text \This method picks a universally quantified assumption and attempts to guess an instantiation for it, backtracking over possible other instantiations and eventually discarding it. Both the conclusion and the premises are used to try to find candidate (type-correct) instantiations.\ method guess_all_elim = (match premises in U[thin]: "\x. P (x :: 'a)" for P \ \(match premises in "?H (y :: 'a)" for y \ \rule allE[where P=P and x=y, OF U]\)?, (match conclusion in "?H (y :: 'a)" for y \ \rule allE[where P=P and x=y, OF U]\)?\) lemma "\x :: 'a. P x \ \x :: 'a. Q x \ Q y" apply (guess_all_elim) back apply assumption done text \This method is similar to above, but instead searches for witnesses for an existential conclusion.\ method guess_ex_intro = (match conclusion in "\x. P (x :: 'a)" for P \ \(match premises in "?H (x :: 'a)" for x \ \rule exI [where x = x and P = P]\)?, (match conclusion in "?H (x :: 'a)" for x \ \rule exI [where x = x and P = P]\)?\) lemma "P y \ Q x \ P x \ \x. Q x \ P x" apply (guess_ex_intro) back apply simp done lemma "\x. x = y" apply (guess_ex_intro) apply (rule refl) done named_theorems subst and dests method prop_solver declares intros elims subst dests = (assumption | rule intros | erule elims | drule dests | subst subst | subst (asm) subst | (erule notE; solves \prop_solver\))+ method fol_solver = ((prop_solver | guess_ex_intro | guess_all_elim ) ; solves \fol_solver\) declare allI[intros] Meson.not_exD[dests] Meson.not_allD[dests] exE[elims] ex_simps[subst] all_simps[subst] lemma "(\x. P x) \ (\x. Q x) \ (\x. Q x \ P x)" and "\x. (P x \ (\x. P x))" and "(\x. \y. R x y) \ (\y. \x. R x y)" by fol_solver+ section \Advanced Methods\ text \In this section we'll walk through creating a practical method for simplifying postconditions in hoare logic.\ text \Here is some toy monadic specification that maintains a distinct list in its state, while keeping its length pre-calculated. Abstract flags "initialized" and "finalized" are included to represent state that this function might have to deal with if it were part of a large program.\ record state = list_state :: "nat list" length_state :: nat initialized_state :: bool finalized_state :: bool definition "length_valid s == length (list_state s) = length_state s" definition "list_distinct s == distinct (list_state s)" definition "valid_state s \ length_valid s \ list_distinct s \ finalized_state s" definition "incr_length == modify (\s. s\length_state := (length_state s) + 1\)" definition finalize :: "(state,unit) nondet_monad" where "finalize \ modify (\s. s\finalized_state := length_valid s \ list_distinct s \ initialized_state s\)" definition initialize :: "(state,unit) nondet_monad" where "initialize \ modify (\s. s\initialized_state := valid_state s\)" text \cons_list performs some abstract initialization, updates the list if the element is not already present, and then does some finalization.\ definition "cons_list x == do initialize; l <- gets list_state; unless (x \ set l) (do modify (\s. s\list_state := x # l\); incr_length od); finalize od" lemma initialize_initial[wp]: "\valid_state\ initialize \\_. initialized_state\" unfolding initialize_def apply wp by (simp add: valid_state_def) lemma initialize_valid_state[wp]: "\valid_state\ initialize \\_. valid_state\" unfolding initialize_def apply wp by (simp add: valid_state_def length_valid_def list_distinct_def) lemma finalize_valid_state[wp]: "\\s. initialized_state s \ length_valid s \ list_distinct s\ finalize \\_. valid_state\" unfolding finalize_def apply wp by (simp add: valid_state_def length_valid_def list_distinct_def) lemma incr_length_length_valid[wp]: "\\s. length (list_state s) = Suc (length_state s)\ incr_length \\_. length_valid\" unfolding incr_length_def apply wp by (simp add: length_valid_def) lemma incr_length_distinct[wp]: "\list_distinct\ incr_length \\_. list_distinct\" unfolding incr_length_def apply wp by (simp add: list_distinct_def) lemma incr_length_finalzed[wp]: "\initialized_state\ incr_length \\_. initialized_state\" unfolding incr_length_def apply wp by simp text \In the final proof we encounter a small wrinkle. The generated postcondition is a bit of a jumble and wp gets stuck. After thinking for a bit, it's clear that this is just valid_state and initialized_state. The standard way to do this is to provide this explicitly with a rule_tac. \ lemma "\valid_state\ cons_list x \\_. valid_state\" unfolding cons_list_def unless_def apply wp apply simp apply (rule_tac Q="\r s. valid_state s \ initialized_state s" in hoare_strengthen_post) apply wp apply simp by (auto simp: valid_state_def length_valid_def list_distinct_def) text \In a large proof with potentially dozens of invariants, it becomes impractical to put them into the proof script implicitly like this. Instead of strengthening, we can add implications to the postcondition. The simplifier then implicitly picks this up and solves those postconditions. \ lemma "\valid_state\ cons_list x \\_. valid_state\" unfolding cons_list_def unless_def apply (rule hoare_pre) apply wp apply simp apply (rule hoare_add_post[OF initialize_initial,rotated]) apply simp apply (rule hoare_strengthen_post[OF initialize_valid_state]) by (auto simp: valid_state_def length_valid_def list_distinct_def) text \Unfortunately we can't directly match out of the wp set, so we make a new named theorem to capture hoare triples that we might want to use to strengthen.\ named_theorems hoare_strengthen text \One possible implementation is to write a method that recursively decomposes the postcondition, trying to find a match from our database. Once found, we add it to the postcondition, simplify, then strip it off again.\ method use_hoare for f :: "('b,'a) nondet_monad" and Q :: "'a \ 'b \ bool" declares hoare_strengthen = (match hoare_strengthen in U: "\_\ f \Q\" \ \rule hoare_add_post[OF U,rotated], simp, (rule hoare_drop_imp[where R=Q])?\ |(match (Q) in "\(r :: 'a) (s :: 'b). Q' r s \ Q'' r s" for Q' Q'' \ \(use_hoare f Q')?, (use_hoare f Q'')?\ \ "\(r :: 'a) (s :: 'b). Q' r s \ Q'' r s" for Q' Q'' \ \(use_hoare f Q')?, (use_hoare f Q'')?\)) declare initialize_initial[hoare_strengthen] thm hoare_strengthen method auto_strengthen_post declares hoare_strengthen = (match conclusion in "\_\ f \Q\" for f Q \ \use_hoare f Q\) text \Like magic, initialized_state disappears!\ lemma "\valid_state\ cons_list x \\_. valid_state\" unfolding cons_list_def unless_def apply (rule hoare_pre) apply wp apply simp apply auto_strengthen_post apply (rule hoare_strengthen_post[OF initialize_valid_state]) by (auto simp: valid_state_def length_valid_def list_distinct_def) text \Alternatively we can turn the implementation inside out. We retrieve strengthening rules that match our function, and then see if our postcondition contains it. Much cleaner, but potentially slower.\ method auto_strengthen_post' declares hoare_strengthen = (match conclusion in "\_\ f \Q\" for f Q \ \match hoare_strengthen in U: "\_\ f \Q'\" for Q' \ \match (Q) in "?H Q'" \ \rule hoare_add_post[OF U,rotated], simp, (rule hoare_drop_imp[where R=Q'])?\\\) lemma "\valid_state\ cons_list x \\_. valid_state\" unfolding cons_list_def unless_def apply (rule hoare_pre) apply wp apply simp apply auto_strengthen_post' apply (rule hoare_strengthen_post[OF initialize_valid_state]) by (auto simp: valid_state_def length_valid_def list_distinct_def) end