(* Title: HOL/Library/While_Combinator.thy Author: Tobias Nipkow Author: Alexander Krauss *) header {* A general ``while'' combinator *} theory While_Combinator imports Main begin subsection {* Partial version *} definition while_option :: "('a => bool) => ('a => 'a) => 'a => 'a option" where "while_option b c s = (if (∃k. ~ b ((c ^^ k) s)) then Some ((c ^^ (LEAST k. ~ b ((c ^^ k) s))) s) else None)" theorem while_option_unfold[code]: "while_option b c s = (if b s then while_option b c (c s) else Some s)" proof cases assume "b s" show ?thesis proof (cases "∃k. ~ b ((c ^^ k) s)") case True then obtain k where 1: "~ b ((c ^^ k) s)" .. with `b s` obtain l where "k = Suc l" by (cases k) auto with 1 have "~ b ((c ^^ l) (c s))" by (auto simp: funpow_swap1) then have 2: "∃l. ~ b ((c ^^ l) (c s))" .. from 1 have "(LEAST k. ~ b ((c ^^ k) s)) = Suc (LEAST l. ~ b ((c ^^ Suc l) s))" by (rule Least_Suc) (simp add: `b s`) also have "... = Suc (LEAST l. ~ b ((c ^^ l) (c s)))" by (simp add: funpow_swap1) finally show ?thesis using True 2 `b s` by (simp add: funpow_swap1 while_option_def) next case False then have "~ (∃l. ~ b ((c ^^ Suc l) s))" by blast then have "~ (∃l. ~ b ((c ^^ l) (c s)))" by (simp add: funpow_swap1) with False `b s` show ?thesis by (simp add: while_option_def) qed next assume [simp]: "~ b s" have least: "(LEAST k. ~ b ((c ^^ k) s)) = 0" by (rule Least_equality) auto moreover have "∃k. ~ b ((c ^^ k) s)" by (rule exI[of _ "0::nat"]) auto ultimately show ?thesis unfolding while_option_def by auto qed lemma while_option_stop2: "while_option b c s = Some t ==> EX k. t = (c^^k) s ∧ ¬ b t" apply(simp add: while_option_def split: if_splits) by (metis (lifting) LeastI_ex) lemma while_option_stop: "while_option b c s = Some t ==> ~ b t" by(metis while_option_stop2) theorem while_option_rule: assumes step: "!!s. P s ==> b s ==> P (c s)" and result: "while_option b c s = Some t" and init: "P s" shows "P t" proof - def k == "LEAST k. ~ b ((c ^^ k) s)" from assms have t: "t = (c ^^ k) s" by (simp add: while_option_def k_def split: if_splits) have 1: "ALL i<k. b ((c ^^ i) s)" by (auto simp: k_def dest: not_less_Least) { fix i assume "i <= k" then have "P ((c ^^ i) s)" by (induct i) (auto simp: init step 1) } thus "P t" by (auto simp: t) qed lemma funpow_commute: "[|∀k' < k. f (c ((c^^k') s)) = c' (f ((c^^k') s))|] ==> f ((c^^k) s) = (c'^^k) (f s)" by (induct k arbitrary: s) auto lemma while_option_commute_invariant: assumes Invariant: "!!s. P s ==> b s ==> P (c s)" assumes TestCommute: "!!s. P s ==> b s = b' (f s)" assumes BodyCommute: "!!s. P s ==> b s ==> f (c s) = c' (f s)" assumes Initial: "P s" shows "map_option f (while_option b c s) = while_option b' c' (f s)" unfolding while_option_def proof (rule trans[OF if_distrib if_cong], safe, unfold option.inject) fix k assume "¬ b ((c ^^ k) s)" with Initial show "∃k. ¬ b' ((c' ^^ k) (f s))" proof (induction k arbitrary: s) case 0 thus ?case by (auto simp: TestCommute intro: exI[of _ 0]) next case (Suc k) thus ?case proof (cases "b s") assume "b s" with Suc.IH[of "c s"] Suc.prems show ?thesis by (metis BodyCommute Invariant comp_apply funpow.simps(2) funpow_swap1) next assume "¬ b s" with Suc show ?thesis by (auto simp: TestCommute intro: exI [of _ 0]) qed qed next fix k assume "¬ b' ((c' ^^ k) (f s))" with Initial show "∃k. ¬ b ((c ^^ k) s)" proof (induction k arbitrary: s) case 0 thus ?case by (auto simp: TestCommute intro: exI[of _ 0]) next case (Suc k) thus ?case proof (cases "b s") assume "b s" with Suc.IH[of "c s"] Suc.prems show ?thesis by (metis BodyCommute Invariant comp_apply funpow.simps(2) funpow_swap1) next assume "¬ b s" with Suc show ?thesis by (auto simp: TestCommute intro: exI [of _ 0]) qed qed next fix k assume k: "¬ b' ((c' ^^ k) (f s))" have *: "(LEAST k. ¬ b' ((c' ^^ k) (f s))) = (LEAST k. ¬ b ((c ^^ k) s))" (is "?k' = ?k") proof (cases ?k') case 0 have "¬ b' ((c' ^^ 0) (f s))" unfolding 0[symmetric] by (rule LeastI[of _ k]) (rule k) hence "¬ b s" by (auto simp: TestCommute Initial) hence "?k = 0" by (intro Least_equality) auto with 0 show ?thesis by auto next case (Suc k') have "¬ b' ((c' ^^ Suc k') (f s))" unfolding Suc[symmetric] by (rule LeastI) (rule k) moreover { fix k assume "k ≤ k'" hence "k < ?k'" unfolding Suc by simp hence "b' ((c' ^^ k) (f s))" by (rule iffD1[OF not_not, OF not_less_Least]) } note b' = this { fix k assume "k ≤ k'" hence "f ((c ^^ k) s) = (c' ^^ k) (f s)" and "b ((c ^^ k) s) = b' ((c' ^^ k) (f s))" and "P ((c ^^ k) s)" by (induct k) (auto simp: b' assms) with `k ≤ k'` have "b ((c ^^ k) s)" and "f ((c ^^ k) s) = (c' ^^ k) (f s)" and "P ((c ^^ k) s)" by (auto simp: b') } note b = this(1) and body = this(2) and inv = this(3) hence k': "f ((c ^^ k') s) = (c' ^^ k') (f s)" by auto ultimately show ?thesis unfolding Suc using b proof (intro Least_equality[symmetric]) case goal1 hence Test: "¬ b' (f ((c ^^ Suc k') s))" by (auto simp: BodyCommute inv b) have "P ((c ^^ Suc k') s)" by (auto simp: Invariant inv b) with Test show ?case by (auto simp: TestCommute) next case goal2 thus ?case by (metis not_less_eq_eq) qed qed have "f ((c ^^ ?k) s) = (c' ^^ ?k') (f s)" unfolding * proof (rule funpow_commute, clarify) fix k assume "k < ?k" hence TestTrue: "b ((c ^^ k) s)" by (auto dest: not_less_Least) from `k < ?k` have "P ((c ^^ k) s)" proof (induct k) case 0 thus ?case by (auto simp: assms) next case (Suc h) hence "P ((c ^^ h) s)" by auto with Suc show ?case by (auto, metis (lifting, no_types) Invariant Suc_lessD not_less_Least) qed with TestTrue show "f (c ((c ^^ k) s)) = c' (f ((c ^^ k) s))" by (metis BodyCommute) qed thus "∃z. (c ^^ ?k) s = z ∧ f z = (c' ^^ ?k') (f s)" by blast qed lemma while_option_commute: assumes "!!s. b s = b' (f s)" "!!s. [|b s|] ==> f (c s) = c' (f s)" shows "map_option f (while_option b c s) = while_option b' c' (f s)" by(rule while_option_commute_invariant[where P = "λ_. True"]) (auto simp add: assms) subsection {* Total version *} definition while :: "('a => bool) => ('a => 'a) => 'a => 'a" where "while b c s = the (while_option b c s)" lemma while_unfold [code]: "while b c s = (if b s then while b c (c s) else s)" unfolding while_def by (subst while_option_unfold) simp lemma def_while_unfold: assumes fdef: "f == while test do" shows "f x = (if test x then f(do x) else x)" unfolding fdef by (fact while_unfold) text {* The proof rule for @{term while}, where @{term P} is the invariant. *} theorem while_rule_lemma: assumes invariant: "!!s. P s ==> b s ==> P (c s)" and terminate: "!!s. P s ==> ¬ b s ==> Q s" and wf: "wf {(t, s). P s ∧ b s ∧ t = c s}" shows "P s ==> Q (while b c s)" using wf apply (induct s) apply simp apply (subst while_unfold) apply (simp add: invariant terminate) done theorem while_rule: "[| P s; !!s. [| P s; b s |] ==> P (c s); !!s. [| P s; ¬ b s |] ==> Q s; wf r; !!s. [| P s; b s |] ==> (c s, s) ∈ r |] ==> Q (while b c s)" apply (rule while_rule_lemma) prefer 4 apply assumption apply blast apply blast apply (erule wf_subset) apply blast done text{* Proving termination: *} theorem wf_while_option_Some: assumes "wf {(t, s). (P s ∧ b s) ∧ t = c s}" and "!!s. P s ==> b s ==> P(c s)" and "P s" shows "EX t. while_option b c s = Some t" using assms(1,3) proof (induction s) case less thus ?case using assms(2) by (subst while_option_unfold) simp qed lemma wf_rel_while_option_Some: assumes wf: "wf R" assumes smaller: "!!s. P s ∧ b s ==> (c s, s) ∈ R" assumes inv: "!!s. P s ∧ b s ==> P(c s)" assumes init: "P s" shows "∃t. while_option b c s = Some t" proof - from smaller have "{(t,s). P s ∧ b s ∧ t = c s} ⊆ R" by auto with wf have "wf {(t,s). P s ∧ b s ∧ t = c s}" by (auto simp: wf_subset) with inv init show ?thesis by (auto simp: wf_while_option_Some) qed theorem measure_while_option_Some: fixes f :: "'s => nat" shows "(!!s. P s ==> b s ==> P(c s) ∧ f(c s) < f s) ==> P s ==> EX t. while_option b c s = Some t" by(blast intro: wf_while_option_Some[OF wf_if_measure, of P b f]) text{* Kleene iteration starting from the empty set and assuming some finite bounding set: *} lemma while_option_finite_subset_Some: fixes C :: "'a set" assumes "mono f" and "!!X. X ⊆ C ==> f X ⊆ C" and "finite C" shows "∃P. while_option (λA. f A ≠ A) f {} = Some P" proof(rule measure_while_option_Some[where f= "%A::'a set. card C - card A" and P= "%A. A ⊆ C ∧ A ⊆ f A" and s= "{}"]) fix A assume A: "A ⊆ C ∧ A ⊆ f A" "f A ≠ A" show "(f A ⊆ C ∧ f A ⊆ f (f A)) ∧ card C - card (f A) < card C - card A" (is "?L ∧ ?R") proof show ?L by(metis A(1) assms(2) monoD[OF `mono f`]) show ?R by (metis A assms(2,3) card_seteq diff_less_mono2 equalityI linorder_le_less_linear rev_finite_subset) qed qed simp lemma lfp_the_while_option: assumes "mono f" and "!!X. X ⊆ C ==> f X ⊆ C" and "finite C" shows "lfp f = the(while_option (λA. f A ≠ A) f {})" proof- obtain P where "while_option (λA. f A ≠ A) f {} = Some P" using while_option_finite_subset_Some[OF assms] by blast with while_option_stop2[OF this] lfp_Kleene_iter[OF assms(1)] show ?thesis by auto qed lemma lfp_while: assumes "mono f" and "!!X. X ⊆ C ==> f X ⊆ C" and "finite C" shows "lfp f = while (λA. f A ≠ A) f {}" unfolding while_def using assms by (rule lfp_the_while_option) blast text{* Computing the reflexive, transitive closure by iterating a successor function. Stops when an element is found that dos not satisfy the test. More refined (and hence more efficient) versions can be found in ITP 2011 paper by Nipkow (the theories are in the AFP entry Flyspeck by Nipkow) and the AFP article Executable Transitive Closures by RenĂ© Thiemann. *} context fixes p :: "'a => bool" and f :: "'a => 'a list" and x :: 'a begin fun rtrancl_while_test :: "'a list × 'a set => bool" where "rtrancl_while_test (ws,_) = (ws ≠ [] ∧ p(hd ws))" fun rtrancl_while_step :: "'a list × 'a set => 'a list × 'a set" where "rtrancl_while_step (ws, Z) = (let x = hd ws; new = remdups (filter (λy. y ∉ Z) (f x)) in (new @ tl ws, set new ∪ Z))" definition rtrancl_while :: "('a list * 'a set) option" where "rtrancl_while = while_option rtrancl_while_test rtrancl_while_step ([x],{x})" fun rtrancl_while_invariant :: "'a list × 'a set => bool" where "rtrancl_while_invariant (ws, Z) = (x ∈ Z ∧ set ws ⊆ Z ∧ distinct ws ∧ {(x,y). y ∈ set(f x)} `` (Z - set ws) ⊆ Z ∧ Z ⊆ {(x,y). y ∈ set(f x)}^* `` {x} ∧ (∀z∈Z - set ws. p z))" lemma rtrancl_while_invariant: assumes inv: "rtrancl_while_invariant st" and test: "rtrancl_while_test st" shows "rtrancl_while_invariant (rtrancl_while_step st)" proof (cases st) fix ws Z assume st: "st = (ws, Z)" with test obtain h t where "ws = h # t" "p h" by (cases ws) auto with inv st show ?thesis by (auto intro: rtrancl.rtrancl_into_rtrancl) qed lemma rtrancl_while_Some: assumes "rtrancl_while = Some(ws,Z)" shows "if ws = [] then Z = {(x,y). y ∈ set(f x)}^* `` {x} ∧ (∀z∈Z. p z) else ¬p(hd ws) ∧ hd ws ∈ {(x,y). y ∈ set(f x)}^* `` {x}" proof - have "rtrancl_while_invariant ([x],{x})" by simp with rtrancl_while_invariant have I: "rtrancl_while_invariant (ws,Z)" by (rule while_option_rule[OF _ assms[unfolded rtrancl_while_def]]) { assume "ws = []" hence ?thesis using I by (auto simp del:Image_Collect_split dest: Image_closed_trancl) } moreover { assume "ws ≠ []" hence ?thesis using I while_option_stop[OF assms[unfolded rtrancl_while_def]] by (simp add: subset_iff) } ultimately show ?thesis by simp qed lemma rtrancl_while_finite_Some: assumes "finite ({(x, y). y ∈ set (f x)}⇧^{*}`` {x})" (is "finite ?Cl") shows "∃y. rtrancl_while = Some y" proof - let ?R = "(λ(_, Z). card (?Cl - Z)) <*mlex*> (λ(ws, _). length ws) <*mlex*> {}" have "wf ?R" by (blast intro: wf_mlex) then show ?thesis unfolding rtrancl_while_def proof (rule wf_rel_while_option_Some[of ?R rtrancl_while_invariant]) fix st assume *: "rtrancl_while_invariant st ∧ rtrancl_while_test st" hence I: "rtrancl_while_invariant (rtrancl_while_step st)" by (blast intro: rtrancl_while_invariant) show "(rtrancl_while_step st, st) ∈ ?R" proof (cases st) fix ws Z let ?ws = "fst (rtrancl_while_step st)" and ?Z = "snd (rtrancl_while_step st)" assume st: "st = (ws, Z)" with * obtain h t where ws: "ws = h # t" "p h" by (cases ws) auto { assume "remdups (filter (λy. y ∉ Z) (f h)) ≠ []" then obtain z where "z ∈ set (remdups (filter (λy. y ∉ Z) (f h)))" by fastforce with st ws I have "Z ⊂ ?Z" "Z ⊆ ?Cl" "?Z ⊆ ?Cl" by auto with assms have "card (?Cl - ?Z) < card (?Cl - Z)" by (blast intro: psubset_card_mono) with st ws have ?thesis unfolding mlex_prod_def by simp } moreover { assume "remdups (filter (λy. y ∉ Z) (f h)) = []" with st ws have "?Z = Z" "?ws = t" by (auto simp: filter_empty_conv) with st ws have ?thesis unfolding mlex_prod_def by simp } ultimately show ?thesis by blast qed qed (simp_all add: rtrancl_while_invariant) qed end hide_const (open) rtrancl_while_test rtrancl_while_step rtrancl_while_invariant hide_fact (open) rtrancl_while_invariant end