diff --git a/thys/Cauchy/CauchysMeanTheorem.thy b/thys/Cauchy/CauchysMeanTheorem.thy --- a/thys/Cauchy/CauchysMeanTheorem.thy +++ b/thys/Cauchy/CauchysMeanTheorem.thy @@ -1,1194 +1,1194 @@ (* Title: Cauchy's Mean Theorem Author: Benjamin Porter , 2006 cleaned up a bit by Tobias Nipkow, 2007 Maintainer: Benjamin Porter *) chapter \Cauchy's Mean Theorem\ theory CauchysMeanTheorem imports Complex_Main begin section \Abstract\ text \The following document presents a proof of Cauchy's Mean theorem formalised in the Isabelle/Isar theorem proving system. {\em Theorem}: For any collection of positive real numbers the geometric mean is always less than or equal to the arithmetic mean. In mathematical terms: $$\sqrt[n]{x_1 x_2 \dots x_n} \leq \frac{x_1 + \dots + x_n}{n}$$ We will use the term {\em mean} to denote the arithmetic mean and {\em gmean} to denote the geometric mean. {\em Informal Proof}: This proof is based on the proof presented in [1]. First we need an auxiliary lemma (the proof of which is presented formally below) that states: Given two pairs of numbers of equal sum, the pair with the greater product is the pair with the least difference. Using this lemma we now present the proof - Given any collection $C$ of positive numbers with mean $M$ and product $P$ and with some element not equal to M we can choose two elements from the collection, $a$ and $b$ where $a>M$ and $b P$ and $M_n = M$, but $C_n$ has all its elements equal to $M$ and thus $P_n = M^n$. Using the definition of geometric and arithmetic means above we can see that for any collection of positive elements $E$ it is always true that gmean E $\leq$ mean E. QED. [1] Dorrie, H. "100 Great Problems of Elementary Mathematics." 1965, Dover. \ section \Formal proof\ (* ============================================================================= *) (* ============================================================================= *) (* ============================================================================= *) subsection \Collection sum and product\ text \The finite collections of numbers will be modelled as lists. We then define sum and product operations over these lists.\ subsubsection \Sum and product definitions\ notation (input) sum_list ("\:_" [999] 998) notation (input) prod_list ("\:_" [999] 998) subsubsection \Properties of sum and product\ text \We now present some useful properties of sum and product over collections.\ text \These lemmas just state that if all the elements in a collection $C$ are less (greater than) than some value $m$, then the sum will less than (greater than) $m*length(C)$.\ lemma sum_list_mono_lt [rule_format]: fixes xs::"real list" shows "xs \ [] \ (\x\ set xs. x < m) \ ((\:xs) < (m*(real (length xs))))" proof (induct xs) case Nil show ?case by simp next case (Cons y ys) { assume ant: "y#ys \ [] \ (\x\set(y#ys). x < m)" hence ylm: "y < m" by simp have "\:(y#ys) < m * real (length (y#ys))" proof cases assume "ys \ []" moreover with ant have "\x\set ys. x < m" by simp moreover with calculation Cons have "\:ys < m*real (length ys)" by simp hence "\:ys + y < m*real(length ys) + y" by simp with ylm have "\:(y#ys) < m*(real(length ys) + 1)" by(simp add:field_simps) then have "\:(y#ys) < m*(real(length ys + 1))" by (simp add: algebra_simps) hence "\:(y#ys) < m*(real (length(y#ys)))" by simp thus ?thesis . next assume "\ (ys \ [])" hence "ys = []" by simp with ylm show ?thesis by simp qed } thus ?case by simp qed lemma sum_list_mono_gt [rule_format]: fixes xs::"real list" shows "xs \ [] \ (\x\set xs. x > m) \ ((\:xs) > (m*(real (length xs))))" txt \proof omitted\ (*<*) proof (induct xs) case Nil show ?case by simp next case (Cons y ys) { assume ant: "y#ys \ [] \ (\x\set(y#ys). x > m)" hence ylm: "y > m" by simp have "\:(y#ys) > m * real (length (y#ys))" proof cases assume "ys \ []" moreover with ant have "\x\set ys. x > m" by simp moreover with calculation Cons have "\:ys > m*real (length ys)" by simp hence "\:ys + y > m*real(length ys) + y" by simp with ylm have "\:(y#ys) > m*(real(length ys) + 1)" by(simp add:field_simps) then have "\:(y#ys) > m*(real(length ys + 1))" by (simp add: algebra_simps) hence "\:(y#ys) > m*(real (length(y#ys)))" by simp thus ?thesis . next assume "\ (ys \ [])" hence "ys = []" by simp with ylm show ?thesis by simp qed } thus ?case by simp (*>*) qed text \If $a$ is in $C$ then the sum of the collection $D$ where $D$ is $C$ with $a$ removed is the sum of $C$ minus $a$.\ lemma sum_list_rmv1: "a \ set xs \ \:(remove1 a xs) = \:xs - (a :: 'a :: ab_group_add)" by (induct xs) auto text \A handy addition and division distribution law over collection sums.\ lemma list_sum_distrib_aux: shows "(\:xs/(n :: 'a :: archimedean_field) + \:xs) = (1 + (1/n)) * \:xs" proof (induct xs) case Nil show ?case by simp next case (Cons x xs) show ?case proof - have "\:(x#xs)/n = x/n + \:xs/n" by (simp add: add_divide_distrib) also with Cons have "\ = x/n + (1+1/n)*\:xs - \:xs" by simp finally have "\:(x#xs) / n + \:(x#xs) = x/n + (1+1/n)*\:xs - \:xs + \:(x#xs)" by simp also have "\ = x/n + (1+(1/n)- 1)*\:xs + \:(x#xs)" by (subst mult_1_left [symmetric, of "\:xs"]) (simp add: field_simps) also have "\ = x/n + (1/n)*\:xs + \:(x#xs)" by simp also have "\ = (1/n)*\:(x#xs) + 1*\:(x#xs)" by(simp add: divide_simps) finally show ?thesis by (simp add: field_simps) qed qed lemma remove1_retains_prod: fixes a and xs::"'a :: comm_ring_1 list" shows "a : set xs \ \:xs = \:(remove1 a xs) * a" (is "?P xs") proof (induct xs) case Nil show ?case by simp next case (Cons aa list) assume plist: "?P list" show "?P (aa#list)" proof assume aml: "a : set(aa#list)" show "\:(aa # list) = \:remove1 a (aa # list) * a" proof (cases) assume aeq: "a = aa" hence "remove1 a (aa#list) = list" by simp hence "\:(remove1 a (aa#list)) = \:list" by simp moreover with aeq have "\:(aa#list) = \:list * a" by simp ultimately show "\:(aa#list) = \:remove1 a (aa # list) * a" by simp next assume naeq: "a \ aa" with aml have aml2: "a : set list" by simp from naeq have "remove1 a (aa#list) = aa#(remove1 a list)" by simp moreover hence "\:(remove1 a (aa#list)) = aa * \:(remove1 a list)" by simp moreover from aml2 plist have "\:list = \:(remove1 a list) * a" by simp ultimately show "\:(aa#list) = \:remove1 a (aa # list) * a" by simp qed qed qed text \The final lemma of this section states that if all elements are positive and non-zero then the product of these elements is also positive and non-zero.\ lemma el_gt0_imp_prod_gt0 [rule_format]: fixes xs::"'a :: archimedean_field list" shows "\y. y : set xs \ y > 0 \ \:xs > 0" proof (induct xs) case Nil show ?case by simp next case (Cons a xs) have exp: "\:(a#xs) = \:xs * a" by simp with Cons have "a > 0" by simp with exp Cons show ?case by simp qed (* ============================================================================= *) (* ============================================================================= *) (* ============================================================================= *) subsection \Auxiliary lemma\ text \This section presents a proof of the auxiliary lemma required for this theorem.\ lemma prod_exp: fixes x::real shows "4*(x*y) = (x+y)^2 - (x-y)^2" by (simp add: power2_diff power2_sum) lemma abs_less_imp_sq_less [rule_format]: fixes x::real and y::real and z::real and w::real assumes diff: "abs (x-y) < abs (z-w)" shows "(x-y)^2 < (z-w)^2" proof cases assume "x=y" hence "abs (x-y) = 0" by simp moreover with diff have "abs(z-w) > 0" by simp hence "(z-w)^2 > 0" by simp ultimately show ?thesis by auto next assume "x\y" hence "abs (x - y) > 0" by simp with diff have "(abs (x-y))^2 < (abs (z-w))^2" by - (drule power_strict_mono [where a="abs (x-y)" and n=2 and b="abs (z-w)"], auto) thus ?thesis by simp qed text \The required lemma (phrased slightly differently than in the informal proof.) Here we show that for any two pairs of numbers with equal sums the pair with the least difference has the greater product.\ lemma le_diff_imp_gt_prod [rule_format]: fixes x::real and y::real and z::real and w::real assumes diff: "abs (x-y) < abs (z-w)" and sum: "x+y = z+w" shows "x*y > z*w" proof - from sum have "(x+y)^2 = (z+w)^2" by simp moreover from diff have "(x-y)^2 < (z-w)^2" by (rule abs_less_imp_sq_less) ultimately have "(x+y)^2 - (x-y)^2 > (z+w)^2 - (z-w)^2" by auto thus "x*y > z*w" by (simp only: prod_exp [symmetric]) qed (* ============================================================================= *) (* ============================================================================= *) (* ============================================================================= *) subsection \Mean and GMean\ text \Now we introduce definitions and properties of arithmetic and geometric means over collections of real numbers.\ subsubsection \Definitions\ text \{\em Arithmetic mean}\ definition mean :: "(real list)\real" where "mean s = (\:s / real (length s))" text \{\em Geometric mean}\ definition gmean :: "(real list)\real" where "gmean s = root (length s) (\:s)" subsubsection \Properties\ -text \Here we present some trival properties of {\em mean} and {\em gmean}.\ +text \Here we present some trivial properties of {\em mean} and {\em gmean}.\ lemma list_sum_mean: fixes xs::"real list" shows "\:xs = ((mean xs) * (real (length xs)))" apply (induct_tac xs) apply simp apply clarsimp apply (unfold mean_def) apply clarsimp done lemma list_mean_eq_iff: fixes one::"real list" and two::"real list" assumes se: "( \:one = \:two )" and le: "(length one = length two)" shows "(mean one = mean two)" proof - from se le have "(\:one / real (length one)) = (\:two / real (length two))" by auto thus ?thesis unfolding mean_def . qed lemma list_gmean_gt_iff: fixes one::"real list" and two::"real list" assumes gz1: "\:one > 0" and gz2: "\:two > 0" and ne1: "one \ []" and ne2: "two \ []" and pe: "(\:one > \:two)" and le: "(length one = length two)" shows "(gmean one > gmean two)" unfolding gmean_def using le ne2 pe by simp text \This slightly more complicated lemma shows that for every non-empty collection with mean $M$, adding another element $a$ where $a=M$ results in a new list with the same mean $M$.\ lemma list_mean_cons [rule_format]: fixes xs::"real list" shows "xs \ [] \ mean ((mean xs)#xs) = mean xs" proof assume lne: "xs \ []" obtain len where ld: "len = real (length xs)" by simp with lne have lgt0: "len > 0" by simp hence lnez: "len \ 0" by simp from lgt0 have l1nez: "len + 1 \ 0" by simp from ld have mean: "mean xs = \:xs / len" unfolding mean_def by simp with ld of_nat_add of_int_1 mean_def have "mean ((mean xs)#xs) = (\:xs/len + \:xs) / (1+len)" by simp also from list_sum_distrib_aux[of xs] have "\ = (1 + (1/len))*\:xs / (1+len)" by simp also with lnez have "\ = (len + 1)*\:xs / (len * (1+len))" apply - apply (drule mult_divide_mult_cancel_left [symmetric, where c="len" and a="(1 + 1 / len) * \:xs" and b="1+len"]) apply (clarsimp simp:field_simps) done also from l1nez have "\ = \:xs / len" apply (subst mult.commute [where a="len"]) apply (drule mult_divide_mult_cancel_left [where c="len+1" and a="\:xs" and b="len"]) by (simp add: ac_simps ac_simps) finally show "mean ((mean xs)#xs) = mean xs" by (simp add: mean) qed text \For a non-empty collection with positive mean, if we add a positive number to the collection then the mean remains positive.\ lemma mean_gt_0 [rule_format]: "xs\[] \ 0 < x \ 0 < (mean xs) \ 0 < (mean (x#xs))" proof assume a: "xs \ [] \ 0 < x \ 0 < mean xs" hence xgt0: "0 < x" and mgt0: "0 < mean xs" by auto from a have lxsgt0: "length xs \ 0" by simp from mgt0 have xsgt0: "0 < \:xs" proof - have "mean xs = \:xs / real (length xs)" unfolding mean_def by simp hence "\:xs = mean xs * real (length xs)" by simp moreover from lxsgt0 have "real (length xs) > 0" by simp moreover with calculation lxsgt0 mgt0 show ?thesis by auto qed with xgt0 have "\:(x#xs) > 0" by simp thus "0 < (mean (x#xs))" proof - assume "0 < \:(x#xs)" moreover have "real (length (x#xs)) > 0" by simp ultimately show ?thesis unfolding mean_def by simp qed qed (* ============================================================================= *) (* ============================================================================= *) (* ============================================================================= *) subsection \\list_neq\, \list_eq\\ text \This section presents a useful formalisation of the act of removing all the elements from a collection that are equal (not equal) to a particular value. We use this to extract all the non-mean elements from a collection as is required by the proof.\ subsubsection \Definitions\ text \\list_neq\ and \list_eq\ just extract elements from a collection that are not equal (or equal) to some value.\ abbreviation list_neq :: "('a list) \ 'a \ ('a list)" where "list_neq xs el == filter (\x. x\el) xs" abbreviation list_eq :: "('a list) \ 'a \ ('a list)" where "list_eq xs el == filter (\x. x=el) xs" subsubsection \Properties\ text \This lemma just proves a required fact about \list_neq\, {\em remove1} and {\em length}.\ lemma list_neq_remove1 [rule_format]: shows "a\m \ a : set xs \ length (list_neq (remove1 a xs) m) < length (list_neq xs m)" (is "?A xs \ ?B xs" is "?P xs") proof (induct xs) case Nil show ?case by simp next case (Cons x xs) note \?P xs\ { assume a: "?A (x#xs)" hence a_ne_m: "a\m" and a_mem_x_xs: "a : set(x#xs)" by auto have b: "?B (x#xs)" proof cases assume "xs = []" with a_ne_m a_mem_x_xs show ?thesis apply (cases "x=a") by auto next assume xs_ne: "xs \ []" with a_ne_m a_mem_x_xs show ?thesis proof cases assume "a=x" with a_ne_m show ?thesis by simp next assume a_ne_x: "a\x" with a_mem_x_xs have a_mem_xs: "a : set xs" by simp with xs_ne a_ne_m Cons have rel: "length (list_neq (remove1 a xs) m) < length (list_neq xs m)" by simp show ?thesis proof cases assume x_e_m: "x=m" with Cons xs_ne a_ne_m a_mem_xs show ?thesis by simp next assume x_ne_m: "x\m" from a_ne_x have "remove1 a (x#xs) = x#(remove1 a xs)" by simp hence "length (list_neq (remove1 a (x#xs)) m) = length (list_neq (x#(remove1 a xs)) m)" by simp also with x_ne_m have "\ = 1 + length (list_neq (remove1 a xs) m)" by simp finally have "length (list_neq (remove1 a (x#xs)) m) = 1 + length (list_neq (remove1 a xs) m)" by simp moreover with x_ne_m a_ne_x have "length (list_neq (x#xs) m) = 1 + length (list_neq xs m)" by simp moreover with rel show ?thesis by simp qed qed qed } thus "?P (x#xs)" by simp qed text \We now prove some facts about \list_eq\, \list_neq\, length, sum and product.\ lemma list_eq_sum [simp]: fixes xs::"real list" shows "\:(list_eq xs m) = (m * (real (length (list_eq xs m))))" apply (induct_tac xs) apply simp apply (simp add:field_simps) done lemma list_eq_prod [simp]: fixes xs::"real list" shows "\:(list_eq xs m) = (m ^ (length (list_eq xs m)))" apply (induct_tac xs) apply simp apply clarsimp done lemma sum_list_split: fixes xs::"real list" shows "\:xs = (\:(list_neq xs m) + \:(list_eq xs m))" apply (induct xs) apply simp apply clarsimp done lemma prod_list_split: fixes xs::"real list" shows "\:xs = (\:(list_neq xs m) * \:(list_eq xs m))" apply (induct xs) apply simp apply clarsimp done lemma sum_list_length_split: fixes xs::"real list" shows "length xs = length (list_neq xs m) + length (list_eq xs m)" apply (induct xs) apply simp+ done (* ============================================================================= *) (* ============================================================================= *) (* ============================================================================= *) subsection \Element selection\ text \We now show that given after extracting all the elements not equal to the mean there exists one that is greater then (or less than) the mean.\ lemma pick_one_gt: fixes xs::"real list" and m::real defines m: "m \ (mean xs)" and neq: "noteq \ list_neq xs m" assumes asum: "noteq\[]" shows "\e. e : set noteq \ e > m" proof (rule ccontr) let ?m = "(mean xs)" let ?neq = "list_neq xs ?m" let ?eq = "list_eq xs ?m" from list_eq_sum have "(\:?eq) = ?m * (real (length ?eq))" by simp from asum have neq_ne: " ?neq \ []" unfolding m neq . assume not_el: "\(\e. e : set noteq \ m < e)" hence not_el_exp: "\(\e. e : set ?neq \ ?m < e)" unfolding m neq . hence "\e. \(e : set ?neq) \ \(e > ?m)" by simp hence "\e. e : set ?neq \ \(e > ?m)" by blast hence "\e. e : set ?neq \ e \ ?m" by (simp add: linorder_not_less) hence "\e. e : set ?neq \ e < ?m" by (simp add:order_le_less) with assms sum_list_mono_lt have "(\:?neq) < ?m * (real (length ?neq))" by blast hence "(\:?neq) + (\:?eq) < ?m * (real (length ?neq)) + (\:?eq)" by simp also have "\ = (?m * ((real (length ?neq) + (real (length ?eq)))))" by (simp add:field_simps) also have "\ = (?m * (real (length xs)))" apply (subst of_nat_add [symmetric]) by (simp add: sum_list_length_split [symmetric]) also have "\ = \:xs" by (simp add: list_sum_mean [symmetric]) also from not_el calculation show False by (simp only: sum_list_split [symmetric]) qed lemma pick_one_lt: fixes xs::"real list" and m::real defines m: "m \ (mean xs)" and neq: "noteq \ list_neq xs m" assumes asum: "noteq\[]" shows "\e. e : set noteq \ e < m" proof (rule ccontr) \ \reductio ad absurdum\ let ?m = "(mean xs)" let ?neq = "list_neq xs ?m" let ?eq = "list_eq xs ?m" from list_eq_sum have "(\:?eq) = ?m * (real (length ?eq))" by simp from asum have neq_ne: " ?neq \ []" unfolding m neq . assume not_el: "\(\e. e : set noteq \ m > e)" hence not_el_exp: "\(\e. e : set ?neq \ ?m > e)" unfolding m neq . hence "\e. \(e : set ?neq) \ \(e < ?m)" by simp hence "\e. e : set ?neq \ \(e < ?m)" by blast hence "\e. e : set ?neq \ e \ ?m" by (simp add: linorder_not_less) hence "\e. e : set ?neq \ e > ?m" by (auto simp: order_le_less) with assms sum_list_mono_gt have "(\:?neq) > ?m * (real (length ?neq))" by blast hence "(\:?neq) + (\:?eq) > ?m * (real (length ?neq)) + (\:?eq)" by simp also have "(?m * (real (length ?neq)) + (\:?eq)) = (?m * (real (length ?neq)) + (?m * (real (length ?eq))))" by simp also have "\ = (?m * ((real (length ?neq) + (real (length ?eq)))))" by (simp add:field_simps) also have "\ = (?m * (real (length xs)))" apply (subst of_nat_add [symmetric]) by (simp add: sum_list_length_split [symmetric]) also have "\ = \:xs" by (simp add: list_sum_mean [symmetric]) also from not_el calculation show False by (simp only: sum_list_split [symmetric]) qed (* =================================================================== *) (* =================================================================== *) (* =================================================================== *) (* =================================================================== *) subsection \Abstract properties\ text \In order to maintain some comprehension of the following proofs we now introduce some properties of collections.\ subsubsection \Definitions\ text \{\em het}: The heterogeneity of a collection is the number of elements not equal to its mean. A heterogeneity of zero implies the all the elements in the collection are the same (i.e. homogeneous).\ definition het :: "real list \ nat" where "het l = length (list_neq l (mean l))" lemma het_gt_0_imp_noteq_ne: "het l > 0 \ list_neq l (mean l) \ []" unfolding het_def by simp lemma het_gt_0I: assumes a: "a \ set xs" and b: "b \ set xs" and neq: "a \ b" shows "het xs > 0" proof (rule ccontr) assume "\ ?thesis" hence "het xs = 0" by auto from this[unfolded het_def] have "list_neq xs (mean xs) = []" by simp from arg_cong[OF this, of set] have mean: "\ x. x \ set xs \ x = mean xs" by auto from mean[OF a] mean[OF b] neq show False by auto qed text \\\-eq\: Two lists are $\gamma$-equivalent if and only if they both have the same number of elements and the same arithmetic means.\ definition \_eq :: "((real list)*(real list)) \ bool" where "\_eq a \ mean (fst a) = mean (snd a) \ length (fst a) = length (snd a)" text \\\_eq\ is transitive and symmetric.\ lemma \_eq_sym: "\_eq (a,b) = \_eq (b,a)" unfolding \_eq_def by auto lemma \_eq_trans: "\_eq (x,y) \ \_eq (y,z) \ \_eq (x,z)" unfolding \_eq_def by simp text \{\em pos}: A list is positive if all its elements are greater than 0.\ definition pos :: "real list \ bool" where "pos l \ (if l=[] then False else \e. e : set l \ e > 0)" lemma pos_empty [simp]: "pos [] = False" unfolding pos_def by simp lemma pos_single [simp]: "pos [x] = (x > 0)" unfolding pos_def by simp lemma pos_imp_ne: "pos xs \ xs\[]" unfolding pos_def by auto lemma pos_cons [simp]: "xs \ [] \ pos (x#xs) = (if (x>0) then pos xs else False)" (is "?P x xs" is "?A xs \ ?S x xs") proof (simp add: if_split, rule impI) assume xsne: "xs \ []" hence pxs_simp: "pos xs = (\e. e : set xs \ e > 0)" unfolding pos_def by simp show "(0 < x \ pos (x # xs) = pos xs) \ (\ 0 < x \ \ pos (x # xs))" proof { assume xgt0: "0 < x" { assume pxs: "pos xs" with pxs_simp have "\e. e : set xs \ e > 0" by simp with xgt0 have "\e. e : set (x#xs) \ e > 0" by simp hence "pos (x#xs)" unfolding pos_def by simp } moreover { assume pxxs: "pos (x#xs)" hence "\e. e : set (x#xs) \ e > 0" unfolding pos_def by simp hence "\e. e : set xs \ e > 0" by simp with xsne have "pos xs" unfolding pos_def by simp } ultimately have "pos (x # xs) = pos xs" apply - apply (rule iffI) apply auto done } thus "0 < x \ pos (x # xs) = pos xs" by simp next { assume xngt0: "\ (0e. e : set xs \ e > 0" by simp with xngt0 have "\ (\e. e : set (x#xs) \ e > 0)" by auto hence "\ (pos (x#xs))" unfolding pos_def by simp } moreover { assume pxxs: "\pos xs" with xsne have "\ (\e. e : set xs \ e > 0)" unfolding pos_def by simp hence "\ (\e. e : set (x#xs) \ e > 0)" by auto hence "\ (pos (x#xs))" unfolding pos_def by simp } ultimately have "\ pos (x#xs)" by auto } thus "\ 0 < x \ \ pos (x # xs)" by simp qed qed subsubsection \Properties\ text \Here we prove some non-trivial properties of the abstract properties.\ text \Two lemmas regarding {\em pos}. The first states the removing an element from a positive collection (of more than 1 element) results in a positive collection. The second asserts that the mean of a positive collection is positive.\ lemma pos_imp_rmv_pos: assumes "(remove1 a xs)\[]" "pos xs" shows "pos (remove1 a xs)" proof - from assms have pl: "pos xs" and rmvne: "(remove1 a xs)\[]" by auto from pl have "xs \ []" by (rule pos_imp_ne) with pl pos_def have "\x. x : set xs \ x > 0" by simp hence "\x. x : set (remove1 a xs) \ x > 0" using set_remove1_subset[of _ xs] by(blast) with rmvne show "pos (remove1 a xs)" unfolding pos_def by simp qed lemma pos_mean: "pos xs \ mean xs > 0" proof (induct xs) case Nil thus ?case by(simp add: pos_def) next case (Cons x xs) show ?case proof cases assume xse: "xs = []" hence "pos (x#xs) = (x > 0)" by simp with Cons(2) have "x>0" by(simp) with xse have "0 < mean (x#xs)" by(auto simp:mean_def) thus ?thesis by simp next assume xsne: "xs \ []" show ?thesis proof cases assume pxs: "pos xs" with Cons(1) have z_le_mxs: "0 < mean xs" by(simp) { assume ass: "x > 0" with ass z_le_mxs xsne have "0 < mean (x#xs)" apply - apply (rule mean_gt_0) by simp } moreover { from xsne pxs have "0 < x" proof cases assume "0 < x" thus ?thesis by simp next assume "\(0 < x)" with xsne pos_cons have "pos (x#xs) = False" by simp with Cons(2) show ?thesis by simp qed } ultimately have "0 < mean (x#xs)" by simp thus ?thesis by simp next assume npxs: "\pos xs" with xsne pos_cons have "pos (x#xs) = False" by simp thus ?thesis using Cons(2) by simp qed qed qed text \We now show that homogeneity of a non-empty collection $x$ implies that its product is equal to \(mean x)^(length x)\.\ lemma prod_list_het0: shows "x\[] \ het x = 0 \ \:x = (mean x) ^ (length x)" proof - assume "x\[] \ het x = 0" hence xne: "x\[]" and hetx: "het x = 0" by auto from hetx have lz: "length (list_neq x (mean x)) = 0" unfolding het_def . hence "\:(list_neq x (mean x)) = 1" by simp with prod_list_split have "\:x = \:(list_eq x (mean x))" apply - apply (drule meta_spec [of _ x]) apply (drule meta_spec [of _ "mean x"]) by simp also with list_eq_prod have "\ = (mean x) ^ (length (list_eq x (mean x)))" by simp also with calculation lz sum_list_length_split have "\:x = (mean x) ^ (length x)" apply - apply (drule meta_spec [of _ x]) apply (drule meta_spec [of _ "mean x"]) by simp thus ?thesis by simp qed text \Furthermore we present an important result - that a homogeneous collection has equal geometric and arithmetic means.\ lemma het_base: shows "pos x \ het x = 0 \ gmean x = mean x" proof - assume ass: "pos x \ het x = 0" hence xne: "x\[]" and hetx: "het x = 0" and posx: "pos x" by auto from posx pos_mean have mxgt0: "mean x > 0" by simp from xne have lxgt0: "length x > 0" by simp with ass prod_list_het0 have "root (length x) (\:x) = root (length x) ((mean x)^(length x))" by simp also from lxgt0 mxgt0 real_root_power_cancel have "\ = mean x" by auto finally show "gmean x = mean x" unfolding gmean_def . qed (* =================================================================== *) (* =================================================================== *) (* =================================================================== *) (* =================================================================== *) subsection \Existence of a new collection\ text \We now present the largest and most important proof in this document. Given any positive and non-homogeneous collection of real numbers there exists a new collection that is $\gamma$-equivalent, positive, has a strictly lower heterogeneity and a greater geometric mean.\ lemma new_list_gt_gmean: fixes xs :: "real list" and m :: real and neq and eq defines m: "m \ mean xs" and neq: "noteq \ list_neq xs m" and eq: "eq \ list_eq xs m" assumes pos_xs: "pos xs" and het_gt_0: "het xs > 0" shows "\xs'. gmean xs' > gmean xs \ \_eq (xs',xs) \ het xs' < het xs \ pos xs'" proof - from pos_xs pos_imp_ne have pos_els: "\y. y : set xs \ y > 0" by (unfold pos_def, simp) with el_gt0_imp_prod_gt0[of xs] have pos_asm: "\:xs > 0" by simp from neq het_gt_0 het_gt_0_imp_noteq_ne m have neqne: "noteq \ []" by simp txt \Pick two elements from xs, one greater than m, one less than m.\ from assms pick_one_gt neqne obtain \ where \_def: "\ : set noteq \ \ > m" unfolding neq m by auto from assms pick_one_lt neqne obtain \ where \_def: "\ : set noteq \ \ < m" unfolding neq m by auto from \_def \_def have \_gt: "\ > m" and \_lt: "\ < m" by auto from \_def \_def have el_neq: "\ \ \" by simp from neqne neq have xsne: "xs \ []" by auto from \_def have \_mem: "\ : set xs" by (auto simp: neq) from \_def have \_mem: "\ : set xs" by (auto simp: neq) from pos_xs pos_def xsne \_mem \_mem \_def \_def have \_pos: "\ > 0" and \_pos: "\ > 0" by auto \ \remove these elements from xs, and insert two new elements\ obtain left_over where lo: "left_over = (remove1 \ (remove1 \ xs))" by simp obtain b where bdef: "m + b = \ + \" by (drule meta_spec [of _ "\ + \ - m"], simp) from m pos_xs pos_def pos_mean have m_pos: "m > 0" by simp with bdef \_pos \_pos \_gt \_lt have b_pos: "b > 0" by simp obtain new_list where nl: "new_list = m#b#(left_over)" by auto from el_neq \_mem \_mem have "\ : set xs \ \ : set xs \ \ \ \" by simp hence "\ : set (remove1 \ xs) \ \ : set(remove1 \ xs)" by (auto simp add: in_set_remove1) moreover hence "(remove1 \ xs) \ [] \ (remove1 \ xs) \ []" by (auto) ultimately have mem : "\ : set(remove1 \ xs) \ \ : set(remove1 \ xs) \ (remove1 \ xs) \ [] \ (remove1 \ xs) \ []" by simp \ \prove that new list is positive\ from nl have nl_pos: "pos new_list" proof cases assume "left_over = []" with nl b_pos m_pos show ?thesis by simp next assume lone: "left_over \ []" from mem pos_imp_rmv_pos pos_xs have "pos (remove1 \ xs)" by simp with lo lone pos_imp_rmv_pos have "pos left_over" by simp with lone mem nl m_pos b_pos show ?thesis by simp qed \ \now show that the new list has the same mean as the old list\ with mem nl lo bdef \_mem \_mem have "\:new_list = \:xs" apply clarsimp apply (subst sum_list_rmv1) apply simp apply (subst sum_list_rmv1) apply simp apply clarsimp done moreover from lo nl \_mem \_mem mem have leq: "length new_list = length xs" apply - apply (erule conjE)+ apply (clarsimp) apply (subst length_remove1, simp) apply (simp add: length_remove1) apply (auto dest!:length_pos_if_in_set) done ultimately have eq_mean: "mean new_list = mean xs" by (rule list_mean_eq_iff) \ \finally show that the new list has a greater gmean than the old list\ have gt_gmean: "gmean new_list > gmean xs" proof - from bdef \_gt \_lt have "abs (m - b) < abs (\ - \)" by arith moreover from bdef have "m+b = \+\" . ultimately have mb_gt_gt: "m*b > \*\" by (rule le_diff_imp_gt_prod) moreover from nl have "\:new_list = \:left_over * (m*b)" by auto moreover from lo \_mem \_mem mem remove1_retains_prod[where 'a = real] have xsprod: "\:xs = \:left_over * (\*\)" by auto moreover from xsne have "xs \ []" . moreover from nl have nlne: "new_list \ []" by simp moreover from pos_asm lo have "\:left_over > 0" proof - from pos_asm have "\:xs > 0" . moreover from xsprod have "\:xs = \:left_over * (\*\)" . ultimately have "\:left_over * (\*\) > 0" by simp moreover from pos_els \_mem \_mem have "\ > 0" and "\ > 0" by auto hence "\*\ > 0" by simp ultimately show "\:left_over > 0" apply - apply (rule zero_less_mult_pos2 [where a="(\ * \)"]) by auto qed ultimately have "\:new_list > \:xs" by simp moreover with pos_asm nl have "\:new_list > 0" by auto moreover from calculation pos_asm xsne nlne leq list_gmean_gt_iff show "gmean new_list > gmean xs" by simp qed \ \auxiliary info\ from \_lt have \_ne_m: "\ \ m" by simp from mem have \_mem_rmv_\: "\ : set (remove1 \ xs)" and rmv_\_ne: "(remove1 \ xs) \ []" by auto from \_def have \_ne_m: "\ \ m" by simp \ \now show that new list is more homogeneous\ have lt_het: "het new_list < het xs" proof cases assume bm: "b=m" with het_def have "het new_list = length (list_neq new_list (mean new_list))" by simp also with m nl eq_mean have "\ = length (list_neq (m#b#(left_over)) m)" by simp also with bm have "\ = length (list_neq left_over m)" by simp also with lo \_def \_def have "\ = length (list_neq (remove1 \ (remove1 \ xs)) m)" by simp also from \_ne_m \_mem_rmv_\ rmv_\_ne have "\ < length (list_neq (remove1 \ xs) m)" apply - apply (rule list_neq_remove1) by simp also from \_mem \_ne_m xsne have "\ < length (list_neq xs m)" apply - apply (rule list_neq_remove1) by simp also with m het_def have "\ = het xs" by simp finally show "het new_list < het xs" . next assume bnm: "b\m" with het_def have "het new_list = length (list_neq new_list (mean new_list))" by simp also with m nl eq_mean have "\ = length (list_neq (m#b#(left_over)) m)" by simp also with bnm have "\ = length (b#(list_neq left_over m))" by simp also have "\ = 1 + length (list_neq left_over m)" by simp also with lo \_def \_def have "\ = 1 + length (list_neq (remove1 \ (remove1 \ xs)) m)" by simp also from \_ne_m \_mem_rmv_\ rmv_\_ne have "\ < 1 + length (list_neq (remove1 \ xs) m)" apply - apply (simp only: nat_add_left_cancel_less) apply (rule list_neq_remove1) by simp finally have "het new_list \ length (list_neq (remove1 \ xs) m)" by simp also from \_mem \_ne_m xsne have "\ < length (list_neq xs m)" apply - apply (rule list_neq_remove1) by simp also with m het_def have "\ = het xs" by simp finally show "het new_list < het xs" . qed \ \thus thesis by existence of newlist\ from \_eq_def lt_het gt_gmean eq_mean leq nl_pos show ?thesis by auto qed text \Furthermore we show that for all non-homogeneous positive collections there exists another collection that is $\gamma$-equivalent, positive, has a greater geometric mean {\em and} is homogeneous.\ lemma existence_of_het0 [rule_format]: shows "\x. p = het x \ p > 0 \ pos x \ (\y. gmean y > gmean x \ \_eq (x,y) \ het y = 0 \ pos y)" (is "?Q p" is "\x. (?A x p \ ?S x)") proof (induct p rule: nat_less_induct) fix n assume ind: "\m 0" and "pos x" by auto with new_list_gt_gmean have "\y. gmean y > gmean x \ \_eq (x,y) \ het y < het x \ pos y" apply - apply (drule meta_spec [of _ x]) apply (drule meta_mp) apply assumption apply (drule meta_mp) apply assumption apply (subst(asm) \_eq_sym) apply simp done then obtain \ where \_def: "gmean \ > gmean x \ \_eq (x,\) \ het \ < het x \ pos \" .. then obtain b where bdef: "b = het \" by simp with ass \_def have "b < n" by auto with ind have "?Q b" by simp with \_def have ind2: "b = het \ \ 0 < b \ pos \ \ (\y. gmean \ < gmean y \ \_eq (\, y) \ het y = 0 \ pos y)" by simp { assume "\(0 = 0" by simp with \_def have "?S x" by auto } moreover { assume "0 < b" with bdef ind2 \_def have "?S \" by simp then obtain \ where "gmean \ < gmean \ \ \_eq (\, \) \ het \ = 0 \ pos \" .. with \_def have "gmean x < gmean \ \ \_eq (x,\) \ het \ = 0 \ pos \" apply clarsimp apply (rule \_eq_trans) by auto hence "?S x" by auto } ultimately have "?S x" by auto } thus "?Q n" by simp qed subsection \Cauchy's Mean Theorem\ text \We now present the final proof of the theorem. For any positive collection we show that its geometric mean is less than or equal to its arithmetic mean.\ theorem CauchysMeanTheorem: fixes z::"real list" assumes "pos z" shows "gmean z \ mean z" proof - from \pos z\ have zne: "z\[]" by (rule pos_imp_ne) show "gmean z \ mean z" proof cases assume "het z = 0" with \pos z\ zne het_base have "gmean z = mean z" by simp thus ?thesis by simp next assume "het z \ 0" hence "het z > 0" by simp moreover obtain k where "k = het z" by simp moreover with calculation \pos z\ existence_of_het0 have "\y. gmean y > gmean z \ \_eq (z,y) \ het y = 0 \ pos y" by auto then obtain \ where "gmean \ > gmean z \ \_eq (z,\) \ het \ = 0 \ pos \" .. with het_base \_eq_def pos_imp_ne have "mean z = mean \" and "gmean \ > gmean z" and "gmean \ = mean \" by auto hence "gmean z < mean z" by simp thus ?thesis by simp qed qed text \In the equality version we prove that the geometric mean is identical to the arithmetic mean iff the collection is homogeneous.\ theorem CauchysMeanTheorem_Eq: fixes z::"real list" assumes "pos z" shows "gmean z = mean z \ het z = 0" proof assume "het z = 0" with het_base[of z] \pos z\ show "gmean z = mean z" by auto next assume eq: "gmean z = mean z" show "het z = 0" proof (rule ccontr) assume "het z \ 0" hence "het z > 0" by auto moreover obtain k where "k = het z" by simp moreover with calculation \pos z\ existence_of_het0 have "\y. gmean y > gmean z \ \_eq (z,y) \ het y = 0 \ pos y" by auto then obtain \ where "gmean \ > gmean z \ \_eq (z,\) \ het \ = 0 \ pos \" .. with het_base \_eq_def pos_imp_ne have "mean z = mean \" and "gmean \ > gmean z" and "gmean \ = mean \" by auto hence "gmean z < mean z" by simp thus False using eq by auto qed qed corollary CauchysMeanTheorem_Less: fixes z::"real list" assumes "pos z" and "het z > 0" shows "gmean z < mean z" using CauchysMeanTheorem[OF \pos z\] CauchysMeanTheorem_Eq[OF \pos z\] \het z > 0\ by auto end diff --git a/thys/HereditarilyFinite/HF.thy b/thys/HereditarilyFinite/HF.thy --- a/thys/HereditarilyFinite/HF.thy +++ b/thys/HereditarilyFinite/HF.thy @@ -1,1107 +1,1107 @@ chapter \The Hereditarily Finite Sets\ theory HF imports "HOL-Library.Nat_Bijection" abbrevs "<:" = "\<^bold>\" and "~<:" = "\<^bold>\" begin text \From "Finite sets and Gödel's Incompleteness Theorems" by S. Swierczkowski. Thanks for Brian Huffman for this development, up to the cases and induct rules.\ section \Basic Definitions and Lemmas\ typedef hf = "UNIV :: nat set" .. definition hfset :: "hf \ hf set" where "hfset a = Abs_hf ` set_decode (Rep_hf a)" definition HF :: "hf set \ hf" where "HF A = Abs_hf (set_encode (Rep_hf ` A))" definition hinsert :: "hf \ hf \ hf" where "hinsert a b = HF (insert a (hfset b))" definition hmem :: "hf \ hf \ bool" (infixl "\<^bold>\" 50) where "hmem a b \ a \ hfset b" abbreviation not_hmem :: "hf \ hf \ bool" (infixl "\<^bold>\" 50) where "a \<^bold>\ b \ \ a \<^bold>\ b" notation (ASCII) hmem (infixl "<:" 50) instantiation hf :: zero begin definition Zero_hf_def: "0 = HF {}" instance .. end lemma Abs_hf_0 [simp]: "Abs_hf 0 = 0" by (simp add: HF_def Zero_hf_def) text \HF Set enumerations\ abbreviation inserthf :: "hf \ hf \ hf" (infixl "\" 60) where "y \ x \ hinsert x y" syntax (ASCII) "_HFinset" :: "args \ hf" ("{|(_)|}") syntax "_HFinset" :: "args \ hf" ("\_\") translations "\x, y\" \ "\y\ \ x" "\x\" \ "0 \ x" lemma finite_hfset [simp]: "finite (hfset a)" unfolding hfset_def by simp lemma HF_hfset [simp]: "HF (hfset a) = a" unfolding HF_def hfset_def by (simp add: image_image Abs_hf_inverse Rep_hf_inverse) lemma hfset_HF [simp]: "finite A \ hfset (HF A) = A" unfolding HF_def hfset_def by (simp add: image_image Abs_hf_inverse Rep_hf_inverse) lemma inj_on_HF: "inj_on HF (Collect finite)" by (metis hfset_HF inj_onI mem_Collect_eq) lemma hmem_hempty [simp]: "a \<^bold>\ 0" unfolding hmem_def Zero_hf_def by simp lemmas hemptyE [elim!] = hmem_hempty [THEN notE] lemma hmem_hinsert [iff]: "hmem a (c \ b) \ a = b \ a \<^bold>\ c" unfolding hmem_def hinsert_def by simp lemma hf_ext: "a = b \ (\x. x \<^bold>\ a \ x \<^bold>\ b)" unfolding hmem_def set_eq_iff [symmetric] by (metis HF_hfset) lemma finite_cases [consumes 1, case_names empty insert]: "\finite F; F = {} \ P; \A x. \F = insert x A; x \ A; finite A\ \ P\ \ P" by (induct F rule: finite_induct, simp_all) lemma hf_cases [cases type: hf, case_names 0 hinsert]: obtains "y = 0" | a b where "y = b \ a" and "a \<^bold>\ b" proof - have "finite (hfset y)" by (rule finite_hfset) thus thesis by (metis Zero_hf_def finite_cases hf_ext hfset_HF hinsert_def hmem_def that) qed lemma Rep_hf_hinsert: "a \<^bold>\ b \ Rep_hf (hinsert a b) = 2 ^ (Rep_hf a) + Rep_hf b" unfolding hinsert_def HF_def hfset_def apply (simp add: image_image Abs_hf_inverse Rep_hf_inverse) apply (subst set_encode_insert, simp) apply (clarsimp simp add: hmem_def hfset_def image_def Rep_hf_inject [symmetric] Abs_hf_inverse, simp) done lemma less_two_power: "n < 2 ^ n" by (induct n, auto) section \Verifying the Axioms of HF\ text \HF1\ lemma hempty_iff: "z=0 \ (\x. x \<^bold>\ z)" by (simp add: hf_ext) text \HF2\ lemma hinsert_iff: "z = x \ y \ (\u. u \<^bold>\ z \ u \<^bold>\ x \ u = y)" by (auto simp: hf_ext) text \HF induction\ lemma hf_induct [induct type: hf, case_names 0 hinsert]: assumes [simp]: "P 0" "\x y. \P x; P y; x \<^bold>\ y\ \ P (y \ x)" shows "P z" proof (induct z rule: wf_induct [where r="measure Rep_hf", OF wf_measure]) case (1 x) show ?case proof (cases x rule: hf_cases) case 0 thus ?thesis by simp next case (hinsert a b) thus ?thesis using 1 by (simp add: Rep_hf_hinsert less_le_trans [OF less_two_power le_add1]) qed qed text \HF3\ lemma hf_induct_ax: "\P 0; \x. P x \ (\y. P y \ P (x \ y))\ \ P x" by (induct x, auto) lemma hf_equalityI [intro]: "(\x. x \<^bold>\ a \ x \<^bold>\ b) \ a = b" by (simp add: hf_ext) lemma hinsert_nonempty [simp]: "A \ a \ 0" by (auto simp: hf_ext) lemma hinsert_commute: "(z \ y) \ x = (z \ x) \ y" by (auto simp: hf_ext) lemma hmem_HF_iff [simp]: "x \<^bold>\ HF A \ x \ A \ finite A" apply (cases "finite A", auto) apply (simp add: hmem_def) apply (simp add: hmem_def) apply (metis HF_def Rep_hf_inject Abs_hf_0 finite_imageD hempty_iff inj_onI set_encode_inf) done section \Ordered Pairs, from ZF/ZF.thy\ lemma singleton_eq_iff [iff]: "\a\ = \b\ \ a=b" by (metis hmem_hempty hmem_hinsert) lemma doubleton_eq_iff: "\a,b\ = \c,d\ \ (a=c \ b=d) \ (a=d \ b=c)" by auto (metis hmem_hempty hmem_hinsert)+ definition hpair :: "hf \ hf \ hf" where "hpair a b = \\a\,\a,b\\" definition hfst :: "hf \ hf" where "hfst p \ THE x. \y. p = hpair x y" definition hsnd :: "hf \ hf" where "hsnd p \ THE y. \x. p = hpair x y" definition hsplit :: "[[hf, hf] \ 'a, hf] \ 'a::{}" \ \for pattern-matching\ where "hsplit c \ \p. c (hfst p) (hsnd p)" text \Ordered Pairs, from ZF/ZF.thy\ nonterminal hfs syntax (ASCII) "_Tuple" :: "[hf, hfs] \ hf" ("<(_,/ _)>") "_hpattern" :: "[pttrn, patterns] \ pttrn" ("<_,/ _>") syntax "" :: "hf \ hfs" ("_") "_Enum" :: "[hf, hfs] \ hfs" ("_,/ _") "_Tuple" :: "[hf, hfs] \ hf" ("\(_,/ _)\") "_hpattern" :: "[pttrn, patterns] \ pttrn" ("\_,/ _\") translations "" \ ">" "" \ "CONST hpair x y" "" \ ">" "\. b" \ "CONST hsplit(\x . b)" "\. b" \ "CONST hsplit(\x y. b)" lemma hpair_def': "hpair a b = \\a,a\,\a,b\\" by (auto simp: hf_ext hpair_def) lemma hpair_iff [simp]: "hpair a b = hpair a' b' \ a=a' \ b=b'" by (auto simp: hpair_def' doubleton_eq_iff) lemmas hpair_inject = hpair_iff [THEN iffD1, THEN conjE, elim!] lemma hfst_conv [simp]: "hfst \a,b\ = a" by (simp add: hfst_def) lemma hsnd_conv [simp]: "hsnd \a,b\ = b" by (simp add: hsnd_def) lemma hsplit [simp]: "hsplit c \a,b\ = c a b" by (simp add: hsplit_def) section \Unions, Comprehensions, Intersections\ subsection \Unions\ text \Theorem 1.5 (Existence of the union of two sets).\ lemma binary_union: "\z. \u. u \<^bold>\ z \ u \<^bold>\ x \ u \<^bold>\ y" proof (induct x rule: hf_induct) case 0 thus ?case by auto next case (hinsert a b) thus ?case by (metis hmem_hinsert) qed text \Theorem 1.6 (Existence of the union of a set of sets).\ lemma union_of_set: "\z. \u. u \<^bold>\ z \ (\y. y \<^bold>\ x \ u \<^bold>\ y)" proof (induct x rule: hf_induct) case 0 thus ?case by (metis hmem_hempty) next case (hinsert a b) then show ?case by (metis hmem_hinsert binary_union [of a]) qed subsection \Set comprehensions\ text \Theorem 1.7, comprehension scheme\ lemma comprehension: "\z. \u. u \<^bold>\ z \ u \<^bold>\ x \ P u" proof (induct x rule: hf_induct) case 0 thus ?case by (metis hmem_hempty) next case (hinsert a b) thus ?case by (metis hmem_hinsert) qed definition HCollect :: "(hf \ bool) \ hf \ hf" \ \comprehension\ where "HCollect P A = (THE z. \u. u \<^bold>\ z = (P u \ u \<^bold>\ A))" syntax (ASCII) "_HCollect" :: "idt \ hf \ bool \ hf" ("(1\_ <:/ _./ _\)") syntax "_HCollect" :: "idt \ hf \ bool \ hf" ("(1\_ \<^bold>\/ _./ _\)") translations "\x \<^bold>\ A. P\" \ "CONST HCollect (\x. P) A" lemma HCollect_iff [iff]: "hmem x (HCollect P A) \ P x \ x \<^bold>\ A" apply (insert comprehension [of A P], clarify) apply (simp add: HCollect_def) apply (rule theI2, blast) apply (auto simp: hf_ext) done lemma HCollectI: "a \<^bold>\ A \ P a \ hmem a \x \<^bold>\ A. P x\" by simp lemma HCollectE: assumes "a \<^bold>\ \x \<^bold>\ A. P x\" obtains "a \<^bold>\ A" "P a" using assms by auto lemma HCollect_hempty [simp]: "HCollect P 0 = 0" by (simp add: hf_ext) subsection \Union operators\ instantiation hf :: sup begin definition "sup a b = (THE z. \u. u \<^bold>\ z \ u \<^bold>\ a \ u \<^bold>\ b)" instance .. end abbreviation hunion :: "hf \ hf \ hf" (infixl "\" 65) where "hunion \ sup" lemma hunion_iff [iff]: "hmem x (a \ b) \ x \<^bold>\ a \ x \<^bold>\ b" apply (insert binary_union [of a b], clarify) apply (simp add: sup_hf_def) apply (rule theI2) apply (auto simp: hf_ext) done definition HUnion :: "hf \ hf" ("\_" [900] 900) where "HUnion A = (THE z. \u. u \<^bold>\ z \ (\y. y \<^bold>\ A \ u \<^bold>\ y))" lemma HUnion_iff [iff]: "hmem x (\ A) \ (\y. y \<^bold>\ A \ x \<^bold>\ y)" apply (insert union_of_set [of A], clarify) apply (simp add: HUnion_def) apply (rule theI2) apply (auto simp: hf_ext) done lemma HUnion_hempty [simp]: "\ 0 = 0" by (simp add: hf_ext) lemma HUnion_hinsert [simp]: "\(A \ a) = a \ \A" by (auto simp: hf_ext) lemma HUnion_hunion [simp]: "\(A \ B) = \A \ \B" by blast subsection \Definition 1.8, Intersections\ instantiation hf :: inf begin definition "inf a b = \x \<^bold>\ a. x \<^bold>\ b\" instance .. end abbreviation hinter :: "hf \ hf \ hf" (infixl "\" 70) where "hinter \ inf" lemma hinter_iff [iff]: "hmem u (x \ y) \ u \<^bold>\ x \ u \<^bold>\ y" by (metis HCollect_iff inf_hf_def) definition HInter :: "hf \ hf" ("\_" [900] 900) where "HInter(A) = \x \<^bold>\ HUnion(A). \y. y \<^bold>\ A \ x \<^bold>\ y\" lemma HInter_hempty [iff]: "\ 0 = 0" by (metis HCollect_hempty HUnion_hempty HInter_def) lemma HInter_iff [simp]: "A\0 \ hmem x (\ A) \ (\y. y \<^bold>\ A \ x \<^bold>\ y)" by (auto simp: HInter_def) lemma HInter_hinsert [simp]: "A\0 \ \(A \ a) = a \ \A" by (auto simp: hf_ext HInter_iff [OF hinsert_nonempty]) subsection \Set Difference\ instantiation hf :: minus begin definition "A - B = \x \<^bold>\ A. x \<^bold>\ B\" instance .. end lemma hdiff_iff [iff]: "hmem u (x - y) \ u \<^bold>\ x \ u \<^bold>\ y" by (auto simp: minus_hf_def) lemma hdiff_zero [simp]: fixes x :: hf shows "(x - 0) = x" by blast lemma zero_hdiff [simp]: fixes x :: hf shows "(0 - x) = 0" by blast lemma hdiff_insert: "A - (B \ a) = A - B - \a\" by blast lemma hinsert_hdiff_if: "(A \ x) - B = (if x \<^bold>\ B then A - B else (A - B) \ x)" by auto section \Replacement\ text \Theorem 1.9 (Replacement Scheme).\ lemma replacement: "(\u v v'. u \<^bold>\ x \ R u v \ R u v' \ v'=v) \ \z. \v. v \<^bold>\ z \ (\u. u \<^bold>\ x \ R u v)" proof (induct x rule: hf_induct) case 0 thus ?case by (metis hmem_hempty) next case (hinsert a b) thus ?case by simp (metis hmem_hinsert) qed lemma replacement_fun: "\z. \v. v \<^bold>\ z \ (\u. u \<^bold>\ x \ v = f u)" by (rule replacement [where R = "\u v. v = f u"]) auto definition PrimReplace :: "hf \ (hf \ hf \ bool) \ hf" where "PrimReplace A R = (THE z. \v. v \<^bold>\ z \ (\u. u \<^bold>\ A \ R u v))" definition Replace :: "hf \ (hf \ hf \ bool) \ hf" where "Replace A R = PrimReplace A (\x y. (\!z. R x z) \ R x y)" definition RepFun :: "hf \ (hf \ hf) \ hf" where "RepFun A f = Replace A (\x y. y = f x)" syntax (ASCII) "_HReplace" :: "[pttrn, pttrn, hf, bool] \ hf" ("(1{|_ ./ _<: _, _|})") "_HRepFun" :: "[hf, pttrn, hf] \ hf" ("(1{|_ ./ _<: _|})" [51,0,51]) "_HINTER" :: "[pttrn, hf, hf] \ hf" ("(3INT _<:_./ _)" 10) "_HUNION" :: "[pttrn, hf, hf] \ hf" ("(3UN _<:_./ _)" 10) syntax "_HReplace" :: "[pttrn, pttrn, hf, bool] \ hf" ("(1\_ ./ _ \<^bold>\ _, _\)") "_HRepFun" :: "[hf, pttrn, hf] \ hf" ("(1\_ ./ _ \<^bold>\ _\)" [51,0,51]) "_HINTER" :: "[pttrn, hf, hf] \ hf" ("(3\_\<^bold>\_./ _)" 10) "_HUNION" :: "[pttrn, hf, hf] \ hf" ("(3\_\<^bold>\_./ _)" 10) translations "\y. x\<^bold>\A, Q\" \ "CONST Replace A (\x y. Q)" "\b. x\<^bold>\A\" \ "CONST RepFun A (\x. b)" "\x\<^bold>\A. B" \ "CONST HInter(CONST RepFun A (\x. B))" "\x\<^bold>\A. B" \ "CONST HUnion(CONST RepFun A (\x. B))" lemma PrimReplace_iff: assumes sv: "\u v v'. u \<^bold>\ A \ R u v \ R u v' \ v'=v" shows "v \<^bold>\ (PrimReplace A R) \ (\u. u \<^bold>\ A \ R u v)" apply (insert replacement [OF sv], clarify) apply (simp add: PrimReplace_def) apply (rule theI2) apply (auto simp: hf_ext) done lemma Replace_iff [iff]: "v \<^bold>\ Replace A R \ (\u. u \<^bold>\ A \ R u v \ (\y. R u y \ y=v))" apply (simp add: Replace_def) apply (subst PrimReplace_iff, auto) done lemma Replace_0 [simp]: "Replace 0 R = 0" by blast lemma Replace_hunion [simp]: "Replace (A \ B) R = Replace A R \ Replace B R" by blast lemma Replace_cong [cong]: "\ A=B; \x y. x \<^bold>\ B \ P x y \ Q x y \ \ Replace A P = Replace B Q" by (simp add: hf_ext cong: conj_cong) lemma RepFun_iff [iff]: "v \<^bold>\ (RepFun A f) \ (\u. u \<^bold>\ A \ v = f u)" by (auto simp: RepFun_def) lemma RepFun_cong [cong]: "\ A=B; \x. x \<^bold>\ B \ f(x)=g(x) \ \ RepFun A f = RepFun B g" by (simp add: RepFun_def) lemma triv_RepFun [simp]: "RepFun A (\x. x) = A" by blast lemma RepFun_0 [simp]: "RepFun 0 f = 0" by blast lemma RepFun_hinsert [simp]: "RepFun (hinsert a b) f = hinsert (f a) (RepFun b f)" by blast lemma RepFun_hunion [simp]: "RepFun (A \ B) f = RepFun A f \ RepFun B f" by blast lemma HF_HUnion: "\finite A; \x. x\A \ finite (B x)\ \ HF (\x \ A. B x) = (\x\<^bold>\HF A. HF (B x))" by (rule hf_equalityI) (auto) section \Subset relation and the Lattice Properties\ text \Definition 1.10 (Subset relation).\ instantiation hf :: order begin definition less_eq_hf where "A \ B \ (\x. x \<^bold>\ A \ x \<^bold>\ B)" definition less_hf where "A < B \ A \ B \ A \ (B::hf)" instance by standard (auto simp: less_eq_hf_def less_hf_def) end subsection \Rules for subsets\ lemma hsubsetI [intro!]: "(\x. x\<^bold>\A \ x\<^bold>\B) \ A \ B" by (simp add: less_eq_hf_def) text \Classical elimination rule\ lemma hsubsetCE [elim]: "\ A \ B; c\<^bold>\A \ P; c\<^bold>\B \ P \ \ P" by (auto simp: less_eq_hf_def) text \Rule in Modus Ponens style\ lemma hsubsetD [elim]: "\ A \ B; c\<^bold>\A \ \ c\<^bold>\B" by (simp add: less_eq_hf_def) text \Sometimes useful with premises in this order\ lemma rev_hsubsetD: "\ c\<^bold>\A; A\B \ \ c\<^bold>\B" by blast lemma contra_hsubsetD: "\ A \ B; c \ B \ \ c \ A" by blast lemma rev_contra_hsubsetD: "\ c \ B; A \ B \ \ c \ A" by blast lemma hf_equalityE: fixes A :: hf shows "A = B \ (A \ B \ B \ A \ P) \ P" by (metis order_refl) subsection \Lattice properties\ instantiation hf :: distrib_lattice begin instance by standard (auto simp: less_eq_hf_def less_hf_def inf_hf_def) end instantiation hf :: bounded_lattice_bot begin definition "bot = (0::hf)" instance by standard (auto simp: less_eq_hf_def bot_hf_def) end lemma hinter_hempty_left [simp]: "0 \ A = 0" by (metis bot_hf_def inf_bot_left) lemma hinter_hempty_right [simp]: "A \ 0 = 0" by (metis bot_hf_def inf_bot_right) lemma hunion_hempty_left [simp]: "0 \ A = A" by (metis bot_hf_def sup_bot_left) lemma hunion_hempty_right [simp]: "A \ 0 = A" by (metis bot_hf_def sup_bot_right) lemma less_eq_hempty [simp]: "u \ 0 \ u = (0::hf)" by (metis hempty_iff less_eq_hf_def) lemma less_eq_insert1_iff [iff]: "(hinsert x y) \ z \ x \<^bold>\ z \ y \ z" by (auto simp: less_eq_hf_def) lemma less_eq_insert2_iff: "z \ (hinsert x y) \ z \ y \ (\u. hinsert x u = z \ x \<^bold>\ u \ u \ y)" proof (cases "x \<^bold>\ z") case True hence u: "hinsert x (z - \x\) = z" by auto show ?thesis proof assume "z \ (hinsert x y)" thus "z \ y \ (\u. hinsert x u = z \ x \<^bold>\ u \ u \ y)" by (simp add: less_eq_hf_def) (metis u hdiff_iff hmem_hinsert) next assume "z \ y \ (\u. hinsert x u = z \ x \<^bold>\ u \ u \ y)" thus "z \ (hinsert x y)" by (auto simp: less_eq_hf_def) qed next case False thus ?thesis by (metis hmem_hinsert less_eq_hf_def) qed lemma zero_le [simp]: "0 \ (x::hf)" by blast lemma hinsert_eq_sup: "b \ a = b \ \a\" by blast lemma hunion_hinsert_left: "hinsert x A \ B = hinsert x (A \ B)" by blast lemma hunion_hinsert_right: "B \ hinsert x A = hinsert x (B \ A)" by blast lemma hinter_hinsert_left: "hinsert x A \ B = (if x \<^bold>\ B then hinsert x (A \ B) else A \ B)" by auto lemma hinter_hinsert_right: "B \ hinsert x A = (if x \<^bold>\ B then hinsert x (B \ A) else B \ A)" by auto section \Foundation, Cardinality, Powersets\ subsection \Foundation\ text \Theorem 1.13: Foundation (Regularity) Property.\ lemma foundation: assumes z: "z \ 0" shows "\w. w \<^bold>\ z \ w \ z = 0" proof - { fix x assume z: "(\w. w \<^bold>\ z \ w \ z \ 0)" have "x \<^bold>\ z \ x \ z = 0" proof (induction x rule: hf_induct) case 0 thus ?case by (metis hinter_hempty_left z) next case (hinsert x y) thus ?case by (metis hinter_hinsert_left z) qed } thus ?thesis using z by (metis z hempty_iff) qed lemma hmem_not_refl: "x \<^bold>\ x" using foundation [of "\x\"] by (metis hinter_iff hmem_hempty hmem_hinsert) lemma hmem_not_sym: "\ (x \<^bold>\ y \ y \<^bold>\ x)" using foundation [of "\x,y\"] by (metis hinter_iff hmem_hempty hmem_hinsert) lemma hmem_ne: "x \<^bold>\ y \ x \ y" by (metis hmem_not_refl) lemma hmem_Sup_ne: "x \<^bold>\ y \ \x \ y" by (metis HUnion_iff hmem_not_sym) lemma hpair_neq_fst: "\a,b\ \ a" by (metis hpair_def hinsert_iff hmem_not_sym) lemma hpair_neq_snd: "\a,b\ \ b" by (metis hpair_def hinsert_iff hmem_not_sym) lemma hpair_nonzero [simp]: "\x,y\ \ 0" by (auto simp: hpair_def) lemma zero_notin_hpair: "0 \<^bold>\ \x,y\" by (auto simp: hpair_def) subsection \Cardinality\ text \First we need to hack the underlying representation\ lemma hfset_0 [simp]: "hfset 0 = {}" by (metis Zero_hf_def finite.emptyI hfset_HF) lemma hfset_hinsert: "hfset (b \ a) = insert a (hfset b)" by (metis finite_insert hinsert_def HF.finite_hfset hfset_HF) lemma hfset_hdiff: "hfset (x - y) = hfset x - hfset y" proof (induct x arbitrary: y rule: hf_induct) case 0 thus ?case by simp next case (hinsert a b) thus ?case by (simp add: hfset_hinsert Set.insert_Diff_if hinsert_hdiff_if hmem_def) qed definition hcard :: "hf \ nat" where "hcard x = card (hfset x)" lemma hcard_0 [simp]: "hcard 0 = 0" by (simp add: hcard_def) lemma hcard_hinsert_if: "hcard (hinsert x y) = (if x \<^bold>\ y then hcard y else Suc (hcard y))" by (simp add: hcard_def hfset_hinsert card_insert_if hmem_def) lemma hcard_union_inter: "hcard (x \ y) + hcard (x \ y) = hcard x + hcard y" apply (induct x arbitrary: y rule: hf_induct) apply (auto simp: hcard_hinsert_if hunion_hinsert_left hinter_hinsert_left) done lemma hcard_hdiff1_less: "x \<^bold>\ z \ hcard (z - \x\) < hcard z" by (simp add: hcard_def hfset_hdiff hfset_hinsert) (metis card_Diff1_less finite_hfset hmem_def) subsection \Powerset Operator\ text \Theorem 1.11 (Existence of the power set).\ lemma powerset: "\z. \u. u \<^bold>\ z \ u \ x" proof (induction x rule: hf_induct) case 0 thus ?case by (metis hmem_hempty hmem_hinsert less_eq_hempty) next case (hinsert a b) then obtain Pb where Pb: "\u. u \<^bold>\ Pb \ u \ b" by auto obtain RPb where RPb: "\v. v \<^bold>\ RPb \ (\u. u \<^bold>\ Pb \ v = hinsert a u)" using replacement_fun .. thus ?case using Pb binary_union [of Pb RPb] apply (simp add: less_eq_insert2_iff, clarify) apply (rule_tac x=z in exI) apply (metis hinsert.hyps less_eq_hf_def) done qed definition HPow :: "hf \ hf" where "HPow x = (THE z. \u. u \<^bold>\ z \ u \ x)" lemma HPow_iff [iff]: "u \<^bold>\ HPow x \ u \ x" apply (insert powerset [of x], clarify) apply (simp add: HPow_def) apply (rule theI2) apply (auto simp: hf_ext) done lemma HPow_mono: "x \ y \ HPow x \ HPow y" by (metis HPow_iff less_eq_hf_def order_trans) lemma HPow_mono_strict: "x < y \ HPow x < HPow y" by (metis HPow_iff HPow_mono less_le_not_le order_eq_iff) lemma HPow_mono_iff [simp]: "HPow x \ HPow y \ x \ y" by (metis HPow_iff HPow_mono hsubsetCE order_refl) lemma HPow_mono_strict_iff [simp]: "HPow x < HPow y \ x < y" by (metis HPow_mono_iff less_le_not_le) section \Bounded Quantifiers\ definition HBall :: "hf \ (hf \ bool) \ bool" where "HBall A P \ (\x. x \<^bold>\ A \ P x)" \ \bounded universal quantifiers\ definition HBex :: "hf \ (hf \ bool) \ bool" where "HBex A P \ (\x. x \<^bold>\ A \ P x)" \ \bounded existential quantifiers\ syntax (ASCII) "_HBall" :: "pttrn \ hf \ bool \ bool" ("(3ALL _<:_./ _)" [0, 0, 10] 10) "_HBex" :: "pttrn \ hf \ bool \ bool" ("(3EX _<:_./ _)" [0, 0, 10] 10) "_HBex1" :: "pttrn \ hf \ bool \ bool" ("(3EX! _<:_./ _)" [0, 0, 10] 10) syntax "_HBall" :: "pttrn \ hf \ bool \ bool" ("(3\_\<^bold>\_./ _)" [0, 0, 10] 10) "_HBex" :: "pttrn \ hf \ bool \ bool" ("(3\_\<^bold>\_./ _)" [0, 0, 10] 10) "_HBex1" :: "pttrn \ hf \ bool \ bool" ("(3\!_\<^bold>\_./ _)" [0, 0, 10] 10) translations "\x\<^bold>\A. P" \ "CONST HBall A (\x. P)" "\x\<^bold>\A. P" \ "CONST HBex A (\x. P)" "\!x\<^bold>\A. P" \ "\!x. x\A \ P" lemma hball_cong [cong]: "\ A=A'; \x. x \<^bold>\ A' \ P(x) \ P'(x) \ \ (\x\<^bold>\A. P(x)) \ (\x\<^bold>\A'. P'(x))" by (simp add: HBall_def) lemma hballI [intro!]: "(\x. x\<^bold>\A \ P x) \ \x\<^bold>\A. P x" by (simp add: HBall_def) lemma hbspec [dest?]: "\x\<^bold>\A. P x \ x\<^bold>\A \ P x" by (simp add: HBall_def) lemma hballE [elim]: "\x\<^bold>\A. P x \ (P x \ Q) \ (x \<^bold>\ A \ Q) \ Q" by (unfold HBall_def) blast lemma hbex_cong [cong]: "\ A=A'; \x. x \<^bold>\ A' \ P(x) \ P'(x) \ \ (\x\<^bold>\A. P(x)) \ (\x\<^bold>\A'. P'(x))" by (simp add: HBex_def cong: conj_cong) lemma hbexI [intro]: "P x \ x\<^bold>\A \ \x\<^bold>\A. P x" by (unfold HBex_def) blast lemma rev_hbexI [intro?]: "x\<^bold>\A \ P x \ \x\<^bold>\A. P x" by (unfold HBex_def) blast lemma bexCI: "(\x\<^bold>\A. \ P x \ P a) \ a\<^bold>\A \ \x\<^bold>\A. P x" by (unfold HBex_def) blast lemma hbexE [elim!]: "\x\<^bold>\A. P x \ (\x. x\<^bold>\A \ P x \ Q) \ Q" by (unfold HBex_def) blast lemma hball_triv [simp]: "(\x\<^bold>\A. P) = ((\x. x\<^bold>\A) \ P)" - \ \Trival rewrite rule.\ + \ \trivial rewrite rule.\ by (simp add: HBall_def) lemma hbex_triv [simp]: "(\x\<^bold>\A. P) = ((\x. x\<^bold>\A) \ P)" \ \Dual form for existentials.\ by (simp add: HBex_def) lemma hbex_triv_one_point1 [simp]: "(\x\<^bold>\A. x = a) = (a\<^bold>\A)" by blast lemma hbex_triv_one_point2 [simp]: "(\x\<^bold>\A. a = x) = (a\<^bold>\A)" by blast lemma hbex_one_point1 [simp]: "(\x\<^bold>\A. x = a \ P x) = (a\<^bold>\A \ P a)" by blast lemma hbex_one_point2 [simp]: "(\x\<^bold>\A. a = x \ P x) = (a\<^bold>\A \ P a)" by blast lemma hball_one_point1 [simp]: "(\x\<^bold>\A. x = a \ P x) = (a\<^bold>\A \ P a)" by blast lemma hball_one_point2 [simp]: "(\x\<^bold>\A. a = x \ P x) = (a\<^bold>\A \ P a)" by blast lemma hball_conj_distrib: "(\x\<^bold>\A. P x \ Q x) \ ((\x\<^bold>\A. P x) \ (\x\<^bold>\A. Q x))" by blast lemma hbex_disj_distrib: "(\x\<^bold>\A. P x \ Q x) \ ((\x\<^bold>\A. P x) \ (\x\<^bold>\A. Q x))" by blast lemma hb_all_simps [simp, no_atp]: "\A P Q. (\x \<^bold>\ A. P x \ Q) \ ((\x \<^bold>\ A. P x) \ Q)" "\A P Q. (\x \<^bold>\ A. P \ Q x) \ (P \ (\x \<^bold>\ A. Q x))" "\A P Q. (\x \<^bold>\ A. P \ Q x) \ (P \ (\x \<^bold>\ A. Q x))" "\A P Q. (\x \<^bold>\ A. P x \ Q) \ ((\x \<^bold>\ A. P x) \ Q)" "\P. (\x \<^bold>\ 0. P x) \ True" "\a B P. (\x \<^bold>\ B \ a. P x) \ (P a \ (\x \<^bold>\ B. P x))" "\P Q. (\x \<^bold>\ HCollect Q A. P x) \ (\x \<^bold>\ A. Q x \ P x)" "\A P. (\ (\x \<^bold>\ A. P x)) \ (\x \<^bold>\ A. \ P x)" by auto lemma hb_ex_simps [simp, no_atp]: "\A P Q. (\x \<^bold>\ A. P x \ Q) \ ((\x \<^bold>\ A. P x) \ Q)" "\A P Q. (\x \<^bold>\ A. P \ Q x) \ (P \ (\x \<^bold>\ A. Q x))" "\P. (\x \<^bold>\ 0. P x) \ False" "\a B P. (\x \<^bold>\ B \ a. P x) \ (P a \ (\x \<^bold>\ B. P x))" "\P Q. (\x \<^bold>\ HCollect Q A. P x) \ (\x \<^bold>\ A. Q x \ P x)" "\A P. (\(\x \<^bold>\ A. P x)) \ (\x \<^bold>\ A. \ P x)" by auto lemma le_HCollect_iff: "A \ \x \<^bold>\ B. P x\ \ A \ B \ (\x \<^bold>\ A. P x)" by blast section \Relations and Functions\ definition is_hpair :: "hf \ bool" where "is_hpair z = (\x y. z = \x,y\)" definition hconverse :: "hf \ hf" where "hconverse(r) = \z. w \<^bold>\ r, \x y. w = \x,y\ \ z = \y,x\\" definition hdomain :: "hf \ hf" where "hdomain(r) = \x. w \<^bold>\ r, \y. w = \x,y\\" definition hrange :: "hf \ hf" where "hrange(r) = hdomain(hconverse(r))" definition hrelation :: "hf \ bool" where "hrelation(r) = (\z. z \<^bold>\ r \ is_hpair z)" definition hrestrict :: "hf \ hf \ hf" \ \Restrict the relation r to the domain A\ where "hrestrict r A = \z \<^bold>\ r. \x \<^bold>\ A. \y. z = \x,y\\" definition nonrestrict :: "hf \ hf \ hf" where "nonrestrict r A = \z \<^bold>\ r. \x \<^bold>\ A. \y. z \ \x,y\\" definition hfunction :: "hf \ bool" where "hfunction(r) = (\x y. \x,y\ \<^bold>\ r \ (\y'. \x,y'\ \<^bold>\ r \ y=y'))" definition app :: "hf \ hf \ hf" where "app f x = (THE y. \x, y\ \<^bold>\ f)" lemma hrestrict_iff [iff]: "z \<^bold>\ hrestrict r A \ z \<^bold>\ r \ (\ x y. z = \x, y\ \ x \<^bold>\ A)" by (auto simp: hrestrict_def) lemma hrelation_0 [simp]: "hrelation 0" by (force simp add: hrelation_def) lemma hrelation_restr [iff]: "hrelation (hrestrict r x)" by (metis hrelation_def hrestrict_iff is_hpair_def) lemma hrelation_hunion [simp]: "hrelation (f \ g) \ hrelation f \ hrelation g" by (auto simp: hrelation_def) lemma hfunction_restr: "hfunction r \ hfunction (hrestrict r x)" by (auto simp: hfunction_def hrestrict_def) lemma hdomain_restr [simp]: "hdomain (hrestrict r x) = hdomain r \ x" by (force simp add: hdomain_def hrestrict_def) lemma hdomain_0 [simp]: "hdomain 0 = 0" by (force simp add: hdomain_def) lemma hdomain_ins [simp]: "hdomain (r \ \x, y\) = hdomain r \ x" by (force simp add: hdomain_def) lemma hdomain_hunion [simp]: "hdomain (f \ g) = hdomain f \ hdomain g" by (simp add: hdomain_def) lemma hdomain_not_mem [iff]: "\hdomain r, a\ \<^bold>\ r" by (metis hdomain_ins hinter_hinsert_right hmem_hinsert hmem_not_refl hunion_hinsert_right sup_inf_absorb) lemma app_singleton [simp]: "app \\x, y\\ x = y" by (simp add: app_def) lemma app_equality: "hfunction f \ \x, y\ \<^bold>\ f \ app f x = y" by (auto simp: app_def hfunction_def intro: the1I2) lemma app_ins2: "x' \ x \ app (f \ \x, y\) x' = app f x'" by (simp add: app_def) lemma hfunction_0 [simp]: "hfunction 0" by (force simp add: hfunction_def) lemma hfunction_ins: "hfunction f \ x \<^bold>\ hdomain f \ hfunction (f\ \x, y\)" by (auto simp: hfunction_def hdomain_def) lemma hdomainI: "\x, y\ \<^bold>\ f \ x \<^bold>\ hdomain f" by (auto simp: hdomain_def) lemma hfunction_hunion: "hdomain f \ hdomain g = 0 \ hfunction (f \ g) \ hfunction f \ hfunction g" by (auto simp: hfunction_def) (metis hdomainI hinter_iff hmem_hempty)+ lemma app_hrestrict [simp]: "x \<^bold>\ A \ app (hrestrict f A) x = app f x" by (simp add: hrestrict_def app_def) section \Operations on families of sets\ definition HLambda :: "hf \ (hf \ hf) \ hf" where "HLambda A b = RepFun A (\x. \x, b x\)" definition HSigma :: "hf \ (hf \ hf) \ hf" where "HSigma A B = (\x\<^bold>\A. \y\<^bold>\B(x). \\x,y\\)" definition HPi :: "hf \ (hf \ hf) \ hf" where "HPi A B = \ f \<^bold>\ HPow(HSigma A B). A \ hdomain(f) \ hfunction(f)\" syntax (ASCII) "_PROD" :: "[pttrn, hf, hf] \ hf" ("(3PROD _<:_./ _)" 10) "_SUM" :: "[pttrn, hf, hf] \ hf" ("(3SUM _<:_./ _)" 10) "_lam" :: "[pttrn, hf, hf] \ hf" ("(3lam _<:_./ _)" 10) syntax "_PROD" :: "[pttrn, hf, hf] \ hf" ("(3\_\<^bold>\_./ _)" 10) "_SUM" :: "[pttrn, hf, hf] \ hf" ("(3\_\<^bold>\_./ _)" 10) "_lam" :: "[pttrn, hf, hf] \ hf" ("(3\_\<^bold>\_./ _)" 10) translations "\x\<^bold>\A. B" \ "CONST HPi A (\x. B)" "\x\<^bold>\A. B" \ "CONST HSigma A (\x. B)" "\x\<^bold>\A. f" \ "CONST HLambda A (\x. f)" subsection \Rules for Unions and Intersections of families\ lemma HUN_iff [simp]: "b \<^bold>\ (\x\<^bold>\A. B(x)) \ (\x\<^bold>\A. b \<^bold>\ B(x))" by auto (*The order of the premises presupposes that A is rigid; b may be flexible*) lemma HUN_I: "\ a \<^bold>\ A; b \<^bold>\ B(a) \ \ b \<^bold>\ (\x\<^bold>\A. B(x))" by auto lemma HUN_E [elim!]: assumes "b \<^bold>\ (\x\<^bold>\A. B(x))" obtains x where "x \<^bold>\ A" "b \<^bold>\ B(x)" using assms by blast lemma HINT_iff: "b \<^bold>\ (\x\<^bold>\A. B(x)) \ (\x\<^bold>\A. b \<^bold>\ B(x)) \ A\0" by (simp add: HInter_def HBall_def) (metis foundation hmem_hempty) lemma HINT_I: "\ \x. x \<^bold>\ A \ b \<^bold>\ B(x); A\0 \ \ b \<^bold>\ (\x\<^bold>\A. B(x))" by (simp add: HINT_iff) lemma HINT_E: "\ b \<^bold>\ (\x\<^bold>\A. B(x)); a \<^bold>\ A \ \ b \<^bold>\ B(a)" by (auto simp: HINT_iff) subsection \Generalized Cartesian product\ lemma HSigma_iff [simp]: "\a,b\ \<^bold>\ HSigma A B \ a \<^bold>\ A \ b \<^bold>\ B(a)" by (force simp add: HSigma_def) lemma HSigmaI [intro!]: "\ a \<^bold>\ A; b \<^bold>\ B(a) \ \ \a,b\ \<^bold>\ HSigma A B" by simp lemmas HSigmaD1 = HSigma_iff [THEN iffD1, THEN conjunct1] lemmas HSigmaD2 = HSigma_iff [THEN iffD1, THEN conjunct2] text \The general elimination rule\ lemma HSigmaE [elim!]: assumes "c \<^bold>\ HSigma A B" obtains x y where "x \<^bold>\ A" "y \<^bold>\ B(x)" "c=\x,y\" using assms by (force simp add: HSigma_def) lemma HSigmaE2 [elim!]: assumes "\a,b\ \<^bold>\ HSigma A B" obtains "a \<^bold>\ A" and "b \<^bold>\ B(a)" using assms by auto lemma HSigma_empty1 [simp]: "HSigma 0 B = 0" by blast instantiation hf :: times begin definition "A * B = HSigma A (\x. B)" instance .. end lemma times_iff [simp]: "\a,b\ \<^bold>\ A * B \ a \<^bold>\ A \ b \<^bold>\ B" by (simp add: times_hf_def) lemma timesI [intro!]: "\ a \<^bold>\ A; b \<^bold>\ B \ \ \a,b\ \<^bold>\ A * B" by simp lemmas timesD1 = times_iff [THEN iffD1, THEN conjunct1] lemmas timesD2 = times_iff [THEN iffD1, THEN conjunct2] text \The general elimination rule\ lemma timesE [elim!]: assumes c: "c \<^bold>\ A * B" obtains x y where "x \<^bold>\ A" "y \<^bold>\ B" "c=\x,y\" using c by (auto simp: times_hf_def) text \...and a specific one\ lemma timesE2 [elim!]: assumes "\a,b\ \<^bold>\ A * B" obtains "a \<^bold>\ A" and "b \<^bold>\ B" using assms by auto lemma times_empty1 [simp]: "0 * B = (0::hf)" by auto lemma times_empty2 [simp]: "A*0 = (0::hf)" by blast lemma times_empty_iff: "A*B=0 \ A=0 \ B=(0::hf)" by (auto simp: times_hf_def hf_ext) instantiation hf :: mult_zero begin instance by standard auto end section \Disjoint Sum\ instantiation hf :: zero_neq_one begin definition One_hf_def: "1 = \0\" instance by standard (auto simp: One_hf_def) end instantiation hf :: plus begin definition "A + B = (\0\ * A) \ (\1\ * B)" instance .. end definition Inl :: "hf\hf" where "Inl(a) \ \0,a\" definition Inr :: "hf\hf" where "Inr(b) \ \1,b\" lemmas sum_defs = plus_hf_def Inl_def Inr_def lemma Inl_nonzero [simp]:"Inl x \ 0" by (metis Inl_def hpair_nonzero) lemma Inr_nonzero [simp]:"Inr x \ 0" by (metis Inr_def hpair_nonzero) text\Introduction rules for the injections (as equivalences)\ lemma Inl_in_sum_iff [iff]: "Inl(a) \<^bold>\ A+B \ a \<^bold>\ A" by (auto simp: sum_defs) lemma Inr_in_sum_iff [iff]: "Inr(b) \<^bold>\ A+B \ b \<^bold>\ B" by (auto simp: sum_defs) text \Elimination rule\ lemma sumE [elim!]: assumes u: "u \<^bold>\ A+B" obtains x where "x \<^bold>\ A" "u=Inl(x)" | y where "y \<^bold>\ B" "u=Inr(y)" using u by (auto simp: sum_defs) text \Injection and freeness equivalences, for rewriting\ lemma Inl_iff [iff]: "Inl(a)=Inl(b) \ a=b" by (simp add: sum_defs) lemma Inr_iff [iff]: "Inr(a)=Inr(b) \ a=b" by (simp add: sum_defs) lemma Inl_Inr_iff [iff]: "Inl(a)=Inr(b) \ False" by (simp add: sum_defs) lemma Inr_Inl_iff [iff]: "Inr(b)=Inl(a) \ False" by (simp add: sum_defs) lemma sum_empty [simp]: "0+0 = (0::hf)" by (auto simp: sum_defs) lemma sum_iff: "u \<^bold>\ A+B \ (\x. x \<^bold>\ A \ u=Inl(x)) \ (\y. y \<^bold>\ B \ u=Inr(y))" by blast lemma sum_subset_iff: fixes A :: hf shows "A+B \ C+D \ A\C \ B\D" by blast lemma sum_equal_iff: fixes A :: hf shows "A+B = C+D \ A=C \ B=D" by (auto simp: hf_ext sum_subset_iff) definition is_hsum :: "hf \ bool" where "is_hsum z = (\x. z = Inl x \ z = Inr x)" definition sum_case :: "(hf \ 'a) \ (hf \ 'a) \ hf \ 'a" where "sum_case f g a \ THE z. (\x. a = Inl x \ z = f x) \ (\y. a = Inr y \ z = g y) \ (\ is_hsum a \ z = undefined)" lemma sum_case_Inl [simp]: "sum_case f g (Inl x) = f x" by (simp add: sum_case_def is_hsum_def) lemma sum_case_Inr [simp]: "sum_case f g (Inr y) = g y" by (simp add: sum_case_def is_hsum_def) lemma sum_case_non [simp]: "\ is_hsum a \ sum_case f g a = undefined" by (simp add: sum_case_def is_hsum_def) lemma is_hsum_cases: "(\x. z = Inl x \ z = Inr x) \ \ is_hsum z" by (auto simp: is_hsum_def) lemma sum_case_split: "P (sum_case f g a) \ (\x. a = Inl x \ P(f x)) \ (\y. a = Inr y \ P(g y)) \ (\ is_hsum a \ P undefined)" by (cases "is_hsum a") (auto simp: is_hsum_def) lemma sum_case_split_asm: "P (sum_case f g a) \ \ ((\x. a = Inl x \ \ P(f x)) \ (\y. a = Inr y \ \ P(g y)) \ (\ is_hsum a \ \ P undefined))" by (auto simp add: sum_case_split) end diff --git a/thys/LLL_Basis_Reduction/Missing_Lemmas.thy b/thys/LLL_Basis_Reduction/Missing_Lemmas.thy --- a/thys/LLL_Basis_Reduction/Missing_Lemmas.thy +++ b/thys/LLL_Basis_Reduction/Missing_Lemmas.thy @@ -1,835 +1,835 @@ (* Authors: Jose Divasón Sebastiaan Joosten René Thiemann Akihisa Yamada License: BSD *) section \Missing lemmas\ text \This theory contains many results that are important but not specific for our development. They could be moved to the stardard library and some other AFP entries.\ theory Missing_Lemmas imports Berlekamp_Zassenhaus.Sublist_Iteration (* for thm upt_append *) Berlekamp_Zassenhaus.Square_Free_Int_To_Square_Free_GFp (* for thm large_mod_0 *) Algebraic_Numbers.Resultant Jordan_Normal_Form.Conjugate Jordan_Normal_Form.Missing_VectorSpace Jordan_Normal_Form.VS_Connect Berlekamp_Zassenhaus.Finite_Field_Factorization_Record_Based (* for transfer rules for thm vec_of_list_Nil *) Berlekamp_Zassenhaus.Berlekamp_Hensel (* for unique_factorization_m_factor *) begin no_notation test_bit (infixl "!!" 100) hide_const(open) module.smult up_ring.monom up_ring.coeff (**** Could be merged to HOL/Rings.thy ****) lemma (in zero_less_one) zero_le_one [simp]: "0 \ 1" by (rule less_imp_le, simp) subclass (in zero_less_one) zero_neq_one by (unfold_locales, simp add: less_imp_neq) class ordered_semiring_1 = Rings.ordered_semiring_0 + monoid_mult + zero_less_one begin subclass semiring_1.. lemma of_nat_ge_zero[intro!]: "of_nat n \ 0" using add_right_mono[of _ _ 1] by (induct n, auto) (* Following lemmas are moved from @{class ordered_idom}. *) lemma zero_le_power [simp]: "0 \ a \ 0 \ a ^ n" by (induct n) simp_all lemma power_mono: "a \ b \ 0 \ a \ a ^ n \ b ^ n" by (induct n) (auto intro: mult_mono order_trans [of 0 a b]) lemma one_le_power [simp]: "1 \ a \ 1 \ a ^ n" using power_mono [of 1 a n] by simp lemma power_le_one: "0 \ a \ a \ 1 \ a ^ n \ 1" using power_mono [of a 1 n] by simp lemma power_gt1_lemma: assumes gt1: "1 < a" shows "1 < a * a ^ n" proof - from gt1 have "0 \ a" by (fact order_trans [OF zero_le_one less_imp_le]) from gt1 have "1 * 1 < a * 1" by simp also from gt1 have "\ \ a * a ^ n" by (simp only: mult_mono \0 \ a\ one_le_power order_less_imp_le zero_le_one order_refl) finally show ?thesis by simp qed lemma power_gt1: "1 < a \ 1 < a ^ Suc n" by (simp add: power_gt1_lemma) lemma one_less_power [simp]: "1 < a \ 0 < n \ 1 < a ^ n" by (cases n) (simp_all add: power_gt1_lemma) lemma power_decreasing: "n \ N \ 0 \ a \ a \ 1 \ a ^ N \ a ^ n" proof (induction N) case (Suc N) then have "a * a^N \ 1 * a^n" if "n \ N" using that by (intro mult_mono) auto then show ?case using Suc by (auto simp add: le_Suc_eq) qed (auto) lemma power_increasing: "n \ N \ 1 \ a \ a ^ n \ a ^ N" proof (induction N) case (Suc N) then have "1 * a^n \ a * a^N" if "n \ N" using that by (intro mult_mono) (auto simp add: order_trans[OF zero_le_one]) then show ?case using Suc by (auto simp add: le_Suc_eq) qed (auto) lemma power_Suc_le_self: "0 \ a \ a \ 1 \ a ^ Suc n \ a" using power_decreasing [of 1 "Suc n" a] by simp end lemma prod_list_nonneg: "(\ x. (x :: 'a :: ordered_semiring_1) \ set xs \ x \ 0) \ prod_list xs \ 0" by (induct xs, auto) subclass (in ordered_idom) ordered_semiring_1 by unfold_locales auto (**** End of lemmas that could be moved to HOL/Rings.thy ****) (* missing lemma on logarithms *) lemma log_prod: assumes "0 < a" "a \ 1" "\ x. x \ X \ 0 < f x" shows "log a (prod f X) = sum (log a o f) X" using assms(3) proof (induct X rule: infinite_finite_induct) case (insert x F) have "log a (prod f (insert x F)) = log a (f x * prod f F)" using insert by simp also have "\ = log a (f x) + log a (prod f F)" by (rule log_mult[OF assms(1-2) insert(4) prod_pos], insert insert, auto) finally show ?case using insert by auto qed auto (* TODO: Jordan_Normal_Form/Missing_Ring.ordered_idom should be redefined *) subclass (in ordered_idom) zero_less_one by (unfold_locales, auto) hide_fact Missing_Ring.zero_less_one (**** The following lemmas could be part of the standard library ****) instance real :: ordered_semiring_strict by (intro_classes, auto) instance real :: linordered_idom.. (*This is a generalisation of thm less_1_mult*) lemma less_1_mult': fixes a::"'a::linordered_semidom" shows "1 < a \ 1 \ b \ 1 < a * b" by (metis le_less less_1_mult mult.right_neutral) lemma upt_minus_eq_append: "i\j \ i\j-k \ [i.. [0.. A" and ff: "\a. a \ A \ f (f a) = a" shows "bij_betw f A A" by (intro bij_betwI[OF f f], simp_all add: ff) lemma range_subsetI: assumes "\x. f x = g (h x)" shows "range f \ range g" using assms by auto lemma Gcd_uminus: fixes A::"int set" assumes "finite A" shows "Gcd A = Gcd (uminus ` A)" using assms by (induct A, auto) lemma aux_abs_int: fixes c :: int assumes "c \ 0" shows "\x\ \ \x * c\" proof - have "abs x = abs x * 1" by simp also have "\ \ abs x * abs c" by (rule mult_left_mono, insert assms, auto) finally show ?thesis unfolding abs_mult by auto qed lemma mod_0_abs_less_imp_0: fixes a::int assumes a1: "[a = 0] (mod m)" and a2: "abs(a)0" using assms by auto thus ?thesis using assms unfolding cong_def using int_mod_pos_eq large_mod_0 zless_imp_add1_zle - by (metis abs_of_nonneg le_less not_less zabs_less_one_iff zmod_trival_iff) + by (metis abs_of_nonneg le_less not_less zabs_less_one_iff zmod_trivial_iff) qed (* an intro version of sum_list_0 *) lemma sum_list_zero: assumes "set xs \ {0}" shows "sum_list xs = 0" using assms by (induct xs, auto) (* About @{const max} *) lemma max_idem [simp]: shows "max a a = a" by (simp add: max_def) lemma hom_max: assumes "a \ b \ f a \ f b" shows "f (max a b) = max (f a) (f b)" using assms by (auto simp: max_def) lemma le_max_self: fixes a b :: "'a :: preorder" assumes "a \ b \ b \ a" shows "a \ max a b" and "b \ max a b" using assms by (auto simp: max_def) lemma le_max: fixes a b :: "'a :: preorder" assumes "c \ a \ c \ b" and "a \ b \ b \ a" shows "c \ max a b" using assms(1) le_max_self[OF assms(2)] by (auto dest: order_trans) fun max_list where "max_list [] = (THE x. False)" (* more convenient than "undefined" *) | "max_list [x] = x" | "max_list (x # y # xs) = max x (max_list (y # xs))" declare max_list.simps(1) [simp del] declare max_list.simps(2-3)[code] lemma max_list_Cons: "max_list (x#xs) = (if xs = [] then x else max x (max_list xs))" by (cases xs, auto) lemma max_list_mem: "xs \ [] \ max_list xs \ set xs" by (induct xs, auto simp: max_list_Cons max_def) lemma mem_set_imp_le_max_list: fixes xs :: "'a :: preorder list" assumes "\a b. a \ set xs \ b \ set xs \ a \ b \ b \ a" and "a \ set xs" shows "a \ max_list xs" proof (insert assms, induct xs arbitrary:a) case Nil with assms show ?case by auto next case (Cons x xs) show ?case proof (cases "xs = []") case False have "x \ max_list xs \ max_list xs \ x" apply (rule Cons(2)) using max_list_mem[of xs] False by auto note 1 = le_max_self[OF this] from Cons have "a = x \ a \ set xs" by auto then show ?thesis proof (elim disjE) assume a: "a = x" show ?thesis by (unfold a max_list_Cons, auto simp: False intro!: 1) next assume "a \ set xs" then have "a \ max_list xs" by (intro Cons, auto) with 1 have "a \ max x (max_list xs)" by (auto dest: order_trans) then show ?thesis by (unfold max_list_Cons, auto simp: False) qed qed (insert Cons, auto) qed lemma le_max_list: fixes xs :: "'a :: preorder list" assumes ord: "\a b. a \ set xs \ b \ set xs \ a \ b \ b \ a" and ab: "a \ b" and b: "b \ set xs" shows "a \ max_list xs" proof- note ab also have "b \ max_list xs" by (rule mem_set_imp_le_max_list, fact ord, fact b) finally show ?thesis. qed lemma max_list_le: fixes xs :: "'a :: preorder list" assumes a: "\x. x \ set xs \ x \ a" and xs: "xs \ []" shows "max_list xs \ a" using max_list_mem[OF xs] a by auto lemma max_list_as_Greatest: assumes "\x y. x \ set xs \ y \ set xs \ x \ y \ y \ x" shows "max_list xs = (GREATEST a. a \ set xs)" proof (cases "xs = []") case True then show ?thesis by (unfold Greatest_def, auto simp: max_list.simps(1)) next case False from assms have 1: "x \ set xs \ x \ max_list xs" for x by (auto intro: le_max_list) have 2: "max_list xs \ set xs" by (fact max_list_mem[OF False]) have "\!x. x \ set xs \ (\y. y \ set xs \ y \ x)" (is "\!x. ?P x") proof (intro ex1I) from 1 2 show "?P (max_list xs)" by auto next fix x assume 3: "?P x" with 1 have "x \ max_list xs" by auto moreover from 2 3 have "max_list xs \ x" by auto ultimately show "x = max_list xs" by auto qed note 3 = theI_unique[OF this,symmetric] from 1 2 show ?thesis by (unfold Greatest_def Cons 3, auto) qed lemma hom_max_list_commute: assumes "xs \ []" and "\x y. x \ set xs \ y \ set xs \ h (max x y) = max (h x) (h y)" shows "h (max_list xs) = max_list (map h xs)" by (insert assms, induct xs, auto simp: max_list_Cons max_list_mem) (*Efficient rev [i.. nat \ nat list" ("(1[_>.._])") where rev_upt_0: "[0>..j] = []" | rev_upt_Suc: "[(Suc i)>..j] = (if i \ j then i # [i>..j] else [])" lemma rev_upt_rec: "[i>..j] = (if i>j then [i>..Suc j] @ [j] else [])" by (induct i, auto) definition rev_upt_aux :: "nat \ nat \ nat list \ nat list" where "rev_upt_aux i j js = [i>..j] @ js" lemma upt_aux_rec [code]: "rev_upt_aux i j js = (if j\i then js else rev_upt_aux i (Suc j) (j#js))" by (induct j, auto simp add: rev_upt_aux_def rev_upt_rec) lemma rev_upt_code[code]: "[i>..j] = rev_upt_aux i j []" by(simp add: rev_upt_aux_def) lemma upt_rev_upt: "rev [j>..i] = [i....i]" by (induct j, auto) lemma length_rev_upt [simp]: "length [i>..j] = i - j" by (induct i) (auto simp add: Suc_diff_le) lemma nth_rev_upt [simp]: "j + k < i \ [i>..j] ! k = i - 1 - k" proof - assume jk_i: "j + k < i" have "[i>..j] = rev [j....n]) ! i = f (m - 1 - i)" proof - have "(map f [m>..n]) ! i = f ([m>..n] ! i)" by (rule nth_map, auto simp add: i) also have "... = f (m - 1 - i)" proof (rule arg_cong[of _ _ f], rule nth_rev_upt) show "n + i < m" using i by linarith qed finally show ?thesis . qed lemma coeff_mult_monom: "coeff (p * monom a d) i = (if d \ i then a * coeff p (i - d) else 0)" using coeff_monom_mult[of a d p] by (simp add: ac_simps) (**** End of the lemmas which may be part of the standard library ****) (**** The following lemmas could be moved to Algebraic_Numbers/Resultant.thy ****) lemma vec_of_poly_0 [simp]: "vec_of_poly 0 = 0\<^sub>v 1" by (auto simp: vec_of_poly_def) lemma vec_index_vec_of_poly [simp]: "i \ degree p \ vec_of_poly p $ i = coeff p (degree p - i)" by (simp add: vec_of_poly_def Let_def) lemma poly_of_vec_vec: "poly_of_vec (vec n f) = Poly (rev (map f [0.. Suc) [0..) = Poly (rev (map (f \ Suc) [0.. = poly_of_vec (vec n (f \ Suc)) + monom (f 0) n" by (fold Suc, simp) also have "\ = poly_of_vec (vec (Suc n) f)" apply (unfold poly_of_vec_def Let_def dim_vec sum.lessThan_Suc) by (auto simp add: Suc_diff_Suc) finally show ?case.. qed lemma sum_list_map_dropWhile0: assumes f0: "f 0 = 0" shows "sum_list (map f (dropWhile ((=) 0) xs)) = sum_list (map f xs)" by (induct xs, auto simp add: f0) lemma coeffs_poly_of_vec: "coeffs (poly_of_vec v) = rev (dropWhile ((=) 0) (list_of_vec v))" proof- obtain n f where v: "v = vec n f" by transfer auto show ?thesis by (simp add: v poly_of_vec_vec) qed lemma poly_of_vec_vCons: "poly_of_vec (vCons a v) = monom a (dim_vec v) + poly_of_vec v" (is "?l = ?r") by (auto intro: poly_eqI simp: coeff_poly_of_vec vec_index_vCons) lemma poly_of_vec_as_Poly: "poly_of_vec v = Poly (rev (list_of_vec v))" by (induct v, auto simp:poly_of_vec_vCons Poly_snoc ac_simps) lemma poly_of_vec_add: assumes "dim_vec a = dim_vec b" shows "poly_of_vec (a + b) = poly_of_vec a + poly_of_vec b" using assms by (auto simp add: poly_eq_iff coeff_poly_of_vec) (*TODO: replace the one in Resultant.thy*) lemma degree_poly_of_vec_less: assumes "0 < dim_vec v" and "dim_vec v \ n" shows "degree (poly_of_vec v) < n" using degree_poly_of_vec_less assms by (auto dest: less_le_trans) lemma (in vec_module) poly_of_vec_finsum: assumes "f \ X \ carrier_vec n" shows "poly_of_vec (finsum V f X) = (\i\X. poly_of_vec (f i))" proof (cases "finite X") case False then show ?thesis by auto next case True show ?thesis proof (insert True assms, induct X rule: finite_induct) case IH: (insert a X) have [simp]: "f x \ carrier_vec n" if x: "x \ X" for x using x IH.prems unfolding Pi_def by auto have [simp]: "f a \ carrier_vec n" using IH.prems unfolding Pi_def by auto have [simp]: "dim_vec (finsum V f X) = n" by simp have [simp]: "dim_vec (f a) = n" by simp show ?case proof (cases "a \ X") case True then show ?thesis by (auto simp: insert_absorb IH) next case False then have "(finsum V f (insert a X)) = f a + (finsum V f X)" by (auto intro: finsum_insert IH) also have "poly_of_vec ... = poly_of_vec (f a) + poly_of_vec (finsum V f X)" by (rule poly_of_vec_add, simp) also have "... = (\i\insert a X. poly_of_vec (f i))" using IH False by (subst sum.insert, auto) finally show ?thesis . qed qed auto qed (*This function transforms a polynomial to a vector of dimension n*) definition "vec_of_poly_n p n = vec n (\i. if i < n - degree p - 1 then 0 else coeff p (n - i - 1))" (* TODO: make it abbreviation? *) lemma vec_of_poly_as: "vec_of_poly_n p (Suc (degree p)) = vec_of_poly p" by (induct p, auto simp: vec_of_poly_def vec_of_poly_n_def) lemma vec_of_poly_n_0 [simp]: "vec_of_poly_n p 0 = vNil" by (auto simp: vec_of_poly_n_def) lemma vec_dim_vec_of_poly_n [simp]: "dim_vec (vec_of_poly_n p n) = n" "vec_of_poly_n p n \ carrier_vec n" unfolding vec_of_poly_n_def by auto lemma dim_vec_of_poly [simp]: "dim_vec (vec_of_poly f) = degree f + 1" by (simp add: vec_of_poly_as[symmetric]) lemma vec_index_of_poly_n: assumes "i < n" shows "vec_of_poly_n p n $ i = (if i < n - Suc (degree p) then 0 else coeff p (n - i - 1))" using assms by (auto simp: vec_of_poly_n_def Let_def) lemma vec_of_poly_n_pCons[simp]: shows "vec_of_poly_n (pCons a p) (Suc n) = vec_of_poly_n p n @\<^sub>v vec_of_list [a]" (is "?l = ?r") proof (unfold vec_eq_iff, intro conjI allI impI) show "dim_vec ?l = dim_vec ?r" by auto show "i < dim_vec ?r \ ?l $ i = ?r $ i" for i by (cases "n - i", auto simp: coeff_pCons less_Suc_eq_le vec_index_of_poly_n) qed lemma vec_of_poly_pCons: shows "vec_of_poly (pCons a p) = (if p = 0 then vec_of_list [a] else vec_of_poly p @\<^sub>v vec_of_list [a])" by (cases "degree p", auto simp: vec_of_poly_as[symmetric]) lemma list_of_vec_of_poly [simp]: "list_of_vec (vec_of_poly p) = (if p = 0 then [0] else rev (coeffs p))" by (induct p, auto simp: vec_of_poly_pCons) lemma poly_of_vec_of_poly_n: assumes p: "degree p n" for i by (rule coeff_eq_0, insert i2 p, simp) ultimately show ?thesis using assms unfolding poly_eq_iff unfolding coeff_poly_of_vec by auto qed lemma vec_of_poly_n0[simp]: "vec_of_poly_n 0 n = 0\<^sub>v n" unfolding vec_of_poly_n_def by auto lemma vec_of_poly_n_add: "vec_of_poly_n (a + b) n = vec_of_poly_n a n + vec_of_poly_n b n" proof (induct n arbitrary: a b) case 0 then show ?case by auto next case (Suc n) then show ?case by (cases a, cases b, auto) qed lemma vec_of_poly_n_poly_of_vec: assumes n: "dim_vec g = n" shows "vec_of_poly_n (poly_of_vec g) n = g" proof (auto simp add: poly_of_vec_def vec_of_poly_n_def assms vec_eq_iff Let_def) have d: "degree (\ii degree (poly_of_vec g)" using n by linarith then show "g $ i = 0" using i1 i2 i3 by (metis (no_types, lifting) Suc_diff_Suc coeff_poly_of_vec diff_Suc_less diff_diff_cancel leD le_degree less_imp_le_nat n neq0_conv) next fix i assume "i < n" thus "coeff (\i\<^sub>v (vec_of_poly_n b n)) = smult a b" using assms by (auto simp add: poly_eq_iff coeff_poly_of_vec vec_of_poly_n_def coeff_eq_0) (*TODO: replace the one in Resultant.thy*) definition vec_of_poly_rev_shifted where "vec_of_poly_rev_shifted p n s j \ vec n (\i. if i \ j \ j \ s + i then coeff p (s + i - j) else 0)" lemma vec_of_poly_rev_shifted_dim[simp]: "dim_vec (vec_of_poly_rev_shifted p n s j) = n" unfolding vec_of_poly_rev_shifted_def by auto lemma col_sylvester_sub: (* TODO: from this directly derive col_sylvester *) assumes j: "j < m + n" shows "col (sylvester_mat_sub m n p q) j = vec_of_poly_rev_shifted p n m j @\<^sub>v vec_of_poly_rev_shifted q m n j" (is "?l = ?r") proof show "dim_vec ?l = dim_vec ?r" by simp fix i assume "i < dim_vec ?r" then have i: "i < m+n" by auto show "?l $ i = ?r $ i" unfolding vec_of_poly_rev_shifted_def apply (subst index_col) using i apply simp using j apply simp apply (subst sylvester_mat_sub_index) using i apply simp using j apply simp apply (cases "i < n") using i apply force using i apply (auto simp: not_less not_le intro!: coeff_eq_0) done qed lemma vec_of_poly_rev_shifted_scalar_prod: fixes p v defines "q \ poly_of_vec v" assumes m: "degree p \ m" and n: "dim_vec v = n" assumes j: "j < m+n" shows "vec_of_poly_rev_shifted p n m (n+m-Suc j) \ v = coeff (p * q) j" (is "?l = ?r") proof - have id1: "\ i. m + i - (n + m - Suc j) = i + Suc j - n" using j by auto let ?g = "\ i. if i \ n + m - Suc j \ n - Suc j \ i then coeff p (i + Suc j - n) * v $ i else 0" have "?thesis = ((\i = 0..i\j. coeff p i * (if j - i < n then v $ (n - Suc (j - i)) else 0)))" (is "_ = (?l = ?r)") unfolding vec_of_poly_rev_shifted_def coeff_mult m scalar_prod_def n q_def coeff_poly_of_vec by (subst sum.cong, insert id1, auto) also have "..." proof - have "?r = (\i\j. (if j - i < n then coeff p i * v $ (n - Suc (j - i)) else 0))" (is "_ = sum ?f _") by (rule sum.cong, auto) also have "sum ?f {..j} = sum ?f ({i. i \ j \ j - i < n} \ {i. i \ j \ \ j - i < n})" (is "_ = sum _ (?R1 \ ?R2)") by (rule sum.cong, auto) also have "\ = sum ?f ?R1 + sum ?f ?R2" by (subst sum.union_disjoint, auto) also have "sum ?f ?R2 = 0" by (rule sum.neutral, auto) also have "sum ?f ?R1 + 0 = sum (\ i. coeff p i * v $ (i + n - Suc j)) ?R1" (is "_ = sum ?F _") by (subst sum.cong, auto simp: ac_simps) also have "\ = sum ?F ((?R1 \ {..m}) \ (?R1 - {..m}))" (is "_ = sum _ (?R \ ?R')") by (rule sum.cong, auto) also have "\ = sum ?F ?R + sum ?F ?R'" by (subst sum.union_disjoint, auto) also have "sum ?F ?R' = 0" proof - { fix x assume "x > m" with m have "?F x = 0" by (subst coeff_eq_0, auto) } thus ?thesis by (subst sum.neutral, auto) qed finally have r: "?r = sum ?F ?R" by simp have "?l = sum ?g ({i. i < n \ i \ n + m - Suc j \ n - Suc j \ i} \ {i. i < n \ \ (i \ n + m - Suc j \ n - Suc j \ i)})" (is "_ = sum _ (?L1 \ ?L2)") by (rule sum.cong, auto) also have "\ = sum ?g ?L1 + sum ?g ?L2" by (subst sum.union_disjoint, auto) also have "sum ?g ?L2 = 0" by (rule sum.neutral, auto) also have "sum ?g ?L1 + 0 = sum (\ i. coeff p (i + Suc j - n) * v $ i) ?L1" (is "_ = sum ?G _") by (subst sum.cong, auto) also have "\ = sum ?G (?L1 \ {i. i + Suc j - n \ m} \ (?L1 - {i. i + Suc j - n \ m}))" (is "_ = sum _ (?L \ ?L')") by (subst sum.cong, auto) also have "\ = sum ?G ?L + sum ?G ?L'" by (subst sum.union_disjoint, auto) also have "sum ?G ?L' = 0" proof - { fix x assume "x + Suc j - n > m" with m have "?G x = 0" by (subst coeff_eq_0, auto) } thus ?thesis by (subst sum.neutral, auto) qed finally have l: "?l = sum ?G ?L" by simp let ?bij = "\ i. i + n - Suc j" { fix x assume x: "j < m + n" "Suc (x + j) - n \ m" "x < n" "n - Suc j \ x" define y where "y = x + Suc j - n" from x have "x + Suc j \ n" by auto with x have xy: "x = ?bij y" unfolding y_def by auto from x have y: "y \ ?R" unfolding y_def by auto have "x \ ?bij ` ?R" unfolding xy using y by blast } note tedious = this show ?thesis unfolding l r by (rule sum.reindex_cong[of ?bij], insert j, auto simp: inj_on_def tedious) qed finally show ?thesis by simp qed lemma sylvester_sub_poly: fixes p q :: "'a :: comm_semiring_0 poly" assumes m: "degree p \ m" assumes n: "degree q \ n" assumes v: "v \ carrier_vec (m+n)" shows "poly_of_vec ((sylvester_mat_sub m n p q)\<^sup>T *\<^sub>v v) = poly_of_vec (vec_first v n) * p + poly_of_vec (vec_last v m) * q" (is "?l = ?r") proof (rule poly_eqI) fix i let ?Tv = "(sylvester_mat_sub m n p q)\<^sup>T *\<^sub>v v" have dim: "dim_vec (vec_first v n) = n" "dim_vec (vec_last v m) = m" "dim_vec ?Tv = n + m" using v by auto have if_distrib: "\ x y z. (if x then y else (0 :: 'a)) * z = (if x then y * z else 0)" by auto show "coeff ?l i = coeff ?r i" proof (cases "i < m+n") case False hence i_mn: "i \ m+n" and i_n: "\x. x \ i \ x < n \ x < n" and i_m: "\x. x \ i \ x < m \ x < m" by auto have "coeff ?r i = (\ x < n. vec_first v n $ (n - Suc x) * coeff p (i - x)) + (\ x < m. vec_last v m $ (m - Suc x) * coeff q (i - x))" (is "_ = sum ?f _ + sum ?g _") unfolding coeff_add coeff_mult Let_def unfolding coeff_poly_of_vec dim if_distrib unfolding atMost_def apply(subst sum.inter_filter[symmetric],simp) apply(subst sum.inter_filter[symmetric],simp) unfolding mem_Collect_eq unfolding i_n i_m unfolding lessThan_def by simp also { fix x assume x: "x < n" have "coeff p (i-x) = 0" apply(rule coeff_eq_0) using i_mn x m by auto hence "?f x = 0" by auto } hence "sum ?f {..T *\<^sub>v v) $ (n + m - Suc i)" unfolding coeff_poly_of_vec dim sum.distrib[symmetric] by auto also have "... = coeff (p * poly_of_vec (vec_first v n) + q * poly_of_vec (vec_last v m)) i" apply(subst index_mult_mat_vec) using True apply simp apply(subst row_transpose) using True apply simp apply(subst col_sylvester_sub) using True apply simp apply(subst vec_first_last_append[of v n m,symmetric]) using v apply(simp add: add.commute) apply(subst scalar_prod_append) apply (rule carrier_vecI,simp)+ apply (subst vec_of_poly_rev_shifted_scalar_prod[OF m],simp) using True apply simp apply (subst add.commute[of n m]) apply (subst vec_of_poly_rev_shifted_scalar_prod[OF n]) apply simp using True apply simp by simp also have "... = (\x\i. (if x < n then vec_first v n $ (n - Suc x) else 0) * coeff p (i - x)) + (\x\i. (if x < m then vec_last v m $ (m - Suc x) else 0) * coeff q (i - x))" unfolding coeff_poly_of_vec[of "vec_first v n",unfolded dim_vec_first,symmetric] unfolding coeff_poly_of_vec[of "vec_last v m",unfolded dim_vec_last,symmetric] unfolding coeff_mult[symmetric] by (simp add: mult.commute) also have "... = coeff ?r i" unfolding coeff_add coeff_mult Let_def unfolding coeff_poly_of_vec dim.. finally show ?thesis. qed qed (**** End of the lemmas which could be moved to Algebraic_Numbers/Resultant.thy ****) (**** The following lemmas could be moved to Computational_Algebra/Polynomial.thy ****) lemma normalize_field [simp]: "normalize (a :: 'a :: {field, semiring_gcd}) = (if a = 0 then 0 else 1)" using unit_factor_normalize by fastforce lemma content_field [simp]: "content (p :: 'a :: {field,semiring_gcd} poly) = (if p = 0 then 0 else 1)" by (induct p, auto simp: content_def) lemma primitive_part_field [simp]: "primitive_part (p :: 'a :: {field,semiring_gcd} poly) = p" by (cases "p = 0", auto intro!: primitive_part_prim) lemma primitive_part_dvd: "primitive_part a dvd a" by (metis content_times_primitive_part dvd_def dvd_refl mult_smult_right) lemma degree_abs [simp]: "degree \p\ = degree p" by (auto simp: abs_poly_def) lemma degree_gcd1: assumes a_not0: "a \ 0" shows "degree (gcd a b) \ degree a" proof - let ?g = "gcd a b" have gcd_dvd_b: "?g dvd a" by simp from this obtain c where a_gc: "a = ?g * c" unfolding dvd_def by auto have g_not0: "?g \0" using a_not0 a_gc by auto have c0: "c \ 0" using a_not0 a_gc by auto have "degree ?g \ degree (?g * c)" by (rule degree_mult_right_le[OF c0]) also have "... = degree a" using a_gc by auto finally show ?thesis . qed lemma primitive_part_neg [simp]: fixes a::"'a :: {factorial_ring_gcd,factorial_semiring_multiplicative} poly" shows "primitive_part (-a) = - primitive_part a" proof - have "primitive_part (-a) = primitive_part (smult (-1) a)" by auto then show ?thesis unfolding primitive_part_smult by (simp add: is_unit_unit_factor) qed lemma content_uminus[simp]: fixes f::"int poly" shows "content (-f) = content f" proof - have "-f = - (smult 1 f)" by auto also have "... = smult (-1) f" using smult_minus_left by auto finally have "content (-f) = content (smult (-1) f)" by auto also have "... = normalize (- 1) * content f" unfolding content_smult .. finally show ?thesis by auto qed lemma pseudo_mod_monic: fixes f g :: "'a::{comm_ring_1,semiring_1_no_zero_divisors} poly" defines "r \ pseudo_mod f g" assumes monic_g: "monic g" shows "\q. f = g * q + r" "r = 0 \ degree r < degree g" proof - let ?cg = "coeff g (degree g)" let ?cge = "?cg ^ (Suc (degree f) - degree g)" define a where "a = ?cge" from r_def[unfolded pseudo_mod_def] obtain q where pdm: "pseudo_divmod f g = (q, r)" by (cases "pseudo_divmod f g") auto have g: "g \ 0" using monic_g by auto from pseudo_divmod[OF g pdm] have id: "smult a f = g * q + r" and "r = 0 \ degree r < degree g" by (auto simp: a_def) have a1: "a = 1" unfolding a_def using monic_g by auto hence id2: "f = g * q + r" using id by auto show "r = 0 \ degree r < degree g" by fact from g have "a \ 0" by (auto simp: a_def) with id2 show "\q. f = g * q + r" by auto qed lemma monic_imp_div_mod_int_poly_degree: fixes p :: "'a::{comm_ring_1,semiring_1_no_zero_divisors} poly" assumes m: "monic u" shows "\q r. p = q*u + r \ (r = 0 \ degree r < degree u)" using pseudo_mod_monic[OF m] using mult.commute by metis corollary monic_imp_div_mod_int_poly_degree2: fixes p :: "'a::{comm_ring_1,semiring_1_no_zero_divisors} poly" assumes m: "monic u" and deg_u: "degree u > 0" shows "\q r. p = q*u + r \ (degree r < degree u)" proof - obtain q r where "p = q * u + r" and r: "(r = 0 \ degree r < degree u)" using monic_imp_div_mod_int_poly_degree[OF m, of p] by auto moreover have "degree r < degree u" using deg_u r by auto ultimately show ?thesis by auto qed (**** End of the lemmas that could be moved to Computational_Algebra/Polynomial.thy ****) (* To be categorized *) lemma (in zero_hom) hom_upper_triangular: "A \ carrier_mat n n \ upper_triangular A \ upper_triangular (map_mat hom A)" by (auto simp: upper_triangular_def) end diff --git a/thys/Store_Buffer_Reduction/Text.thy b/thys/Store_Buffer_Reduction/Text.thy --- a/thys/Store_Buffer_Reduction/Text.thy +++ b/thys/Store_Buffer_Reduction/Text.thy @@ -1,1066 +1,1066 @@ (*<*) theory Text imports Variants begin (*>*) section \Programming discipline \label{sec:discipline}\ text \ For sequential code on a single processor the store buffer is invisible, since reads respect outstanding writes in the buffer. This argument can be extended to thread local memory in the context of a multiprocessor architecture. Memory typically becomes temporarily thread local by means of locking. The C-idiom to identify shared portions of the memory is the \texttt{volatile} tag on variables and type declarations. Thread local memory can be accessed non-volatilely, whereas accesses to shared memory are tagged as volatile. This prevents the compiler from applying certain optimizations to those accesses which could cause undesired behavior, \eg to store intermediate values in registers instead of writing them to the memory. The basic idea behind the programming discipline is, that before gathering new information about the shared state (via reading) the thread has to make its outstanding changes to the shared state visible to others (by flushing the store buffer). This allows to sequentialize the reads and writes to obtain a sequentially consistent execution of the global system. In this sequentialization a write to shared memory happens when the write instruction exits the store buffer, and a read from the shared memory happens when all preceding writes have exited. We distinguish thread local and shared memory by an ownership model. Ownership is maintained in ghost state and can be transferred as side effect of write operations and by a dedicated ghost operation. Every thread has a set of owned addresses. Owned addresses of different threads are disjoint. Moreover, there is a global set of shared addresses which can additionally be marked as read-only. Unowned addresses --- addresses owned by no thread --- can be accessed concurrently by all threads. They are a subset of the shared addresses. The read-only addresses are a subset of the unowned addresses (and thus of the shared addresses). We only allow a thread to write to owned addresses and unowned, read-write addresses. We only allow a thread to read from owned addresses and from shared addresses (even if they are owned by another thread). All writes to shared memory have to be volatile. Reads from shared addresses also have to be volatile, except if the address is owned (\ie single writer, multiple readers) or if the address is read-only. Moreover, non-volatile writes are restricted to owned, unshared memory. As long as a thread owns an address it is guaranteed that it is the only one writing to that address. Hence this thread can safely perform non-volatile reads to that address without missing any write. Similar it is safe for any thread to access read-only memory via non-volatile reads since there are no outstanding writes at all. Recall that a volatile read is \Def{clean} if it is guaranteed that there is no outstanding volatile write (to any address) in the store buffer. Moreover every non-volatile read is clean. To regain sequential consistency under the presence of store buffers every thread has to make sure that every read is clean, by flushing the store buffer when necessary. To check the flushing policy of a thread, we keep track of clean reads by means of ghost state. For every thread we maintain a dirty flag. It is reset as the store buffer gets flushed. Upon a volatile write the dirty flag is set. The dirty flag is considered to guarantee that a volatile read is clean. Table \ref{tab:access-grid} summarizes the access policy and Table \ref{tab:flushing} the associated flushing policy of the programming discipline. The key motivation is to improve performance by minimizing the number of store buffer flushes, while staying sequentially consistent. The need for flushing the store buffer decreases from interlocked accesses (where flushing is a side-effect) over volatile accesses to non-volatile accesses. From the viewpoint of access rights there is no difference between interlocked and volatile accesses. However, keep in mind that some interlocked operations can read from, modify and write to an address in a single atomic step of the underlying hardware and are typically used in lock-free algorithms or for the implementation of locks. \begin{table} \centering \caption{Programming discipline.} \captionsetup[table]{position=top} \captionsetup[subtable]{position=top} \newcommand{\mycomment}[1]{} \subfloat[Access policy\label{tab:access-grid}]{ \begin{tabular}{m{1.2cm}@ {\hspace{2mm}}m{1.7cm}@ {\hspace{3mm}}m{1.8cm}m{2.2 cm}} \toprule & shared & shared & unshared \\ & (read-write) & (read-only) & \\ \midrule unowned & \mycomment{iRW, iR, iW,} vR, vW & \mycomment{iR,} vR, R & unreachable\\ owned & \mycomment{iRW, iR, iW,} vR, vW, R & unreachable & \mycomment{iRW, iR, iW,} vR, vW, R, W \\ owned \mbox{by other} & \mycomment{iR,} vR & unreachable & \\ \bottomrule \multicolumn{4}{l}{(v)olatile, (R)ead, (W)rite}\\ \multicolumn{4}{l}{all reads have to be clean } \end{tabular} %\caption{Access policy \label{tab:access-grid}} }\hspace{0.3cm} % %\end{table} % %\begin{table} % \subfloat[Flushing policy\label{tab:flushing}]{ \begin{tabular}{lc} \toprule & flush (before) \\ \midrule interlocked & as side effect \\ vR & if not clean \\ R, vW, W & never \\ \bottomrule \end{tabular} %\caption{Flushing policy \label{tab:flushing}} } \end{table} \ section \Formalization \label{sec:formalization}\ text \ In this section we go into the details of our formalization. In our model, we distinguish the plain `memory system' from the `programming language semantics' which we both describe as a small-step transition relation. During a computation the programming language issues memory instructions (read / write) to the memory system, which itself returns the results in temporary registers. This clean interface allows us to parameterize the program semantics over the memory system. Our main theorem allows us to simulate a computation step in the semantics based on a memory system with store buffers by @{term "n"} steps in the semantics based on a sequentially consistent memory system. We refer to the former one as \Def{store buffer machine} and to the latter one as \Def{virtual machine}. The simulation theorem is independent of the programming language. We continue with introducing the common parts of both machines. In Section \ref{sec:storebuffermachine} we describe the store buffer machine and in Section \ref{sec:virtualmachine} we then describe the virtual machine. The main reduction theorem is presented in \ref{sec:reduction}. \medskip Addresses @{term "a"}, values @{term "v"} and temporaries @{term "t"} are natural numbers. Ghost annotations for manipulating the ownership information are the following sets of addresses: the acquired addresses @{term "A"}, the unshared (local) fraction @{term "L"} of the acquired addresses, the released addresses @{term "R"} and the writable fraction @{term "W"} of the released addresses (the remaining addresses are considered read-only). These ownership annotations are considered as side-effects on volatile writes and interlocked operations (in case a write is performed). Moreover, a special ghost instruction allows to transfer ownership. The possible status changes of an address due to these ownership transfer operations are depicted in Figure \ref{fig:ownership-transfer}. Note that ownership of an address is not directly transferred between threads, but is first released by one thread and then can be acquired by another thread. % \begin{figure} \begin{center} \begin{tikzpicture} [auto, outernode/.style = {rectangle, rounded corners, draw, text centered, minimum height=3cm, minimum width=2.7cm, fill=gray!20}, innernode/.style = {rectangle, rounded corners, draw, text centered, minimum height=1cm, minimum width=1cm, text width=1.5cm, fill=white} ] \node[outernode] (owned) {}; \node[innernode] (oshared) [below] at ($ (owned.north) -(0,0.2cm) $) {shared read-write}; \node[innernode] (onshared) [above] at ($ (owned.south) +(0,0.2cm) $) {unshared}; \node[above] at (owned.north) {owned}; \node[outernode] (unowned) [right] at ($ (owned.east) +(1.5cm,0cm) $) {}; \node[innernode] (rwshared) [below] at ($ (unowned.north) -(0,0.2cm) $) {shared read-write}; \node[innernode] (roshared) [above] at ($ (unowned.south) +(0,0.2cm) $) {shared read-only}; \node[above] at (unowned.north) {unowned}; \path (rwshared.east) -- coordinate (middlex) (oshared.west); \draw[->] (owned.east |- rwshared.170) -- (rwshared.170); \node [above] at (rwshared.170 -| middlex) {@{term "R \ W"}}; \draw[->] (unowned.west |- oshared.350) -- (oshared.350); \node [below] at (oshared.350 -| middlex) {@{term "A \ - L"}}; \draw[->] (unowned.west |- onshared.350) -- (onshared.350); \node [below] at (onshared.350 -| middlex) {@{term "A \ L"}}; \draw[->] (owned.east |- roshared.170) -- (roshared.170); \node [above] at (roshared.170 -| middlex) {@{term "R \ - W"}}; \draw[->] (oshared.292) -- node {@{term "A \ L"}} (onshared.68); \draw[->] (onshared.84) -- node {@{term "A \ - L"}} (oshared.276); \node (legende) [below right] at (owned.south west) {(A)cquire, keep (L)ocal; (R)elease, mark (W)riteable }; \end{tikzpicture} \end{center} \caption{Ownership transfer \label{fig:ownership-transfer}} \end{figure} % A memory instruction is a datatype with the following constructors: \begin{itemize} \item @{term "Read volatile a t"} for reading from address @{term "a"} to temporary @{term "t"}, where the Boolean @{term "volatile"} determines whether the access is volatile or not. \item @{term "Write volatile a sop A L R W"} to write the result of evaluating the store operation @{term "sop"} at address @{term "a"}. A store operation is a pair @{term "(D,f)"}, with the domain @{term "D"} and the function @{term "f"}. The function @{term "f"} takes temporaries @{term "\"} as a parameter, which maps a temporary to a value. The subset of temporaries that is considered by function @{term "f"} is specified by the domain @{term "D"}. We consider store operations as valid when they only depend on their domain: @{thm [display]"valid_sop_def" [simplified conj_to_impl [symmetric], no_vars]} Again the Boolean @{term "volatile"} specifies the kind of memory access. \item @{term "RMW a t sop cond ret A L R W"}, for atomic interlocked `read-modify-write' instructions (flushing the store buffer). First the value at address @{term "a"} is loaded to temporary @{term "t"}, and then the condition @{term "cond"} on the temporaries is considered to decide whether a store operation is also executed. In case of a store the function @{term "ret"}, depending on both the old value at address @{term "a"} and the new value (according to store operation @{term "sop"}), specifies the final result stored in temporary @{term "t"}. With a trivial condition @{term "cond"} this instruction also covers interlocked reads and writes. \item @{term "Fence"}, a memory fence that flushes the store buffer. %todo: rename to flush? \item @{term "Ghost A L R W"} for ownership transfer. \end{itemize} \ subsection \Store buffer machine \label{sec:storebuffermachine}\ text (in program) \ For the store buffer machine the configuration of a single thread is a tuple @{term "(p, is, \, sb)"} consisting of the program state @{term "p"}, a memory instruction list @{term "is"}, the map of temporaries @{term "\"} and the store buffer @{term "sb"}. A global configuration of the store buffer machine @{term "(ts, m)"} consists of a list of thread configurations @{term "ts"} and the memory @{term "m"}, which is a function from addresses to values. We describe the computation of the global system by the non-deterministic transition relation @{term "(ts, m, ()) \\<^sub>s\<^sub>b (ts', m',())"} defined in Figure~\ref{fig:global-transitions}. \begin{figure}[H] \begin{center} @{thm [mode=Rule] store_buffer.concurrent_step.Program [where \="()" and \="()" and \="()" and \="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] store_buffer.concurrent_step.Memop [where \="()" and \="()" and \="()" and \="()" and \'="()" and \'="()" and \'="()" and \'="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] store_buffer.concurrent_step.StoreBuffer [where \="()" and \="()" and \="()" and \="()" and \'="()" and \'="()" and \'="()",no_vars]} \end{center} \caption{Global transitions of store buffer machine\label{fig:global-transitions}} \end{figure} A transition selects a thread @{term "ts!i = (p,is,\,sb,(),())"} and either the `program' the `memory' or the `store buffer' makes a step defined by sub-relations. The program step relation is a parameter to the global transition relation. A program step @{thm (prem 3) "store_buffer.concurrent_step.Program" [no_vars]} takes the temporaries @{term "\"} and the current program state @{term "p"} and makes a step by returning a new program state @{term "p'"} and an instruction list @{term "is'"} which is appended to the remaining instructions. A memory step @{thm (prem 3) "store_buffer.concurrent_step.Memop" [where \="()" and \="()" and \="()" and \="()" and \'="()" and \'="()" and \'="()" and \'="()",no_vars]} of a machine with store buffer may only fill its store buffer with new writes. In a store buffer step @{thm (prem 3) "store_buffer.concurrent_step.StoreBuffer" [where \="()" and \="()" and \="()" and \="()" and \'="()" and \'="()" and \'="()",no_vars]} the store buffer may release outstanding writes to the memory. The store buffer maintains the list of outstanding memory writes. Write instructions are appended to the end of the store buffer and emerge to memory from the front of the list. A read instructions is satisfied from the store buffer if possible. An entry in the store buffer is of the form @{term "Write\<^sub>s\<^sub>b volatile a sop v"} for an outstanding write (keeping the volatile flag), where operation @{term "sop"} evaluated to value @{term "v"}. As defined in Figure \ref{fig:store-buffer-transition} a write updates the memory when it exits the store buffer. % \begin{figure} \begin{center} @{thm [mode=Axiom] SBWrite\<^sub>s\<^sub>b [where rs=sb and \="()" and \="()" and \="()", no_vars]}\\[0.5\baselineskip] \end{center} \caption{Store buffer transition \label{fig:store-buffer-transition}} \end{figure} % The memory transition are defined in Figure \ref{fig:store-buffer-memory}. % \begin{figure} \begin{center} @{thm [mode=Rule] SBRead [where ghst="((),(),(),())",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SBWrite' [where ghst="((),(),(),())",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SBRMWReadOnly' [where ghst="((),(),(),())",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SBRMWWrite' [where ghst="((),(),(),())",no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] sb_memop_step.SBFence [where \="()" and \="()" and \="()" and \="()",no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] sb_memop_step.SBGhost [where \="()" and \="()" and \="()" and \="()",no_vars]} \end{center} \caption{Memory transitions of store buffer machine\label{fig:store-buffer-memory}} \end{figure} % With @{term "buffered_val sb a"} we obtain the value of the last write to address @{term "a"} which is still pending in the store buffer. In case no outstanding write is in the store buffer we read from the memory. Store operations have no immediate effect on the memory but are queued in the store buffer instead. Interlocked operations and the fence operation require an empty store buffer, which means that it has to be flushed before the action can take place. The read-modify-write instruction first adds the current value at address @{term "a"} to temporary @{term "t"} and then checks the store condition @{term "cond"} on the temporaries. If it fails this read is the final result of the operation. Otherwise the store is performed. The resulting value of the temporary @{term "t"} is specified by the function @{term "ret"} which considers both the old and new value as input. The fence and the ghost instruction are just skipped. \ subsection \Virtual machine \label{sec:virtualmachine}\ text (in program) \ The virtual machine is a sequentially consistent machine without store buffers, maintaining additional ghost state to check for the programming discipline. A thread configuration is a tuple @{term "(p, is, \, (), \, \,())"}, with a dirty flag @{term "\"} indicating whether there may be an outstanding volatile write in the store buffer and the set of owned addresses @{term "\"}. The dirty flag @{term "\"} is considered to specify if a read is clean: for \emph{all} volatile reads the dirty flag must not be set. The global configuration of the virtual machine @{term "(ts, m,\)"} maintains a Boolean map of shared addresses @{term "\"} (indicating write permission). Addresses in the domain of mapping @{term "\"} are considered shared and the set of read-only addresses is obtained from @{term "\"} by: @{thm "read_only_def" [no_vars]} According to the rules in Fig \ref{fig:global-virtual-step} a global transition of the virtual machine @{term "(ts, m, \) \\<^sub>v (ts', m', \')"} is either a program or a memory step. % \begin{figure} \begin{center} @{thm [mode=Rule] virtual.concurrent_step.Program [where sb="()" and \="()", no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] virtual.concurrent_step.Memop [where sb="()" and sb'="()" and \="()" and \'="()",no_vars]} \end{center} \caption{Global transitions of virtual machine \label{fig:global-virtual-step}} \end{figure} The transition rules for its memory system are defined in Figure~\ref{fig:virtual-memory}. % \begin{figure} \begin{center} @{thm [mode=Axiom] VRead [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] VWriteNonVolatile [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] VWriteVolatile [where \="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] VRMWReadOnly [where \="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] VRMWWrite [where \="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] VFence [where \="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] VGhost [where \="()", no_vars]}\\[0.1\baselineskip] \end{center} \caption{Memory transitions of the virtual machine \label{fig:virtual-memory}} \end{figure} % In addition to the transition rules for the virtual machine we introduce the \emph{safety} judgment @{term "\s,i\ (is, \, m, \, \, \)\"} in Figure~\ref{fig:safe-virtual-memory}, where @{term "\s"} is the list of ownership sets obtained from the thread list @{term "ts"} and @{term "i"} is the index of the current thread. Safety of all reachable states of the virtual machine ensures that the programming discipline is obeyed by the program and is our formal prerequisite for the simulation theorem. It is left as a proof obligation to be discharged by means of a proper program logic for sequentially consistent executions. % \begin{figure} \begin{center} @{thm [mode=Rule] safe_direct_memop_state.Read [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] safe_direct_memop_state.WriteNonVolatile [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SafeWriteVolatile [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SafeRMWReadOnly [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SafeRMWWrite [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] safe_direct_memop_state.Fence [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] safe_direct_memop_state.Ghost [no_vars]}\\[0.1\baselineskip] \end{center} \caption{Safe configurations of a virtual machine \label{fig:safe-virtual-memory}} \end{figure} % % In the following we elaborate on the rules of Figures \ref{fig:virtual-memory} and \ref{fig:safe-virtual-memory} in parallel. To read from an address it either has to be owned or read-only or it has to be volatile and shared. Moreover the read has to be clean. %TODO: mention the difference distinction of 'single writer' that covered by this: for 'owned and shared' non-volatile read is ok The memory content of address @{term "a"} is stored in temporary @{term "t"}. % Non-volatile writes are only allowed to owned and unshared addresses. The result is written directly into the memory. % A volatile write is only allowed when no other thread owns the address and the address is not marked as read-only. Simultaneously with the volatile write we can transfer ownership as specified by the annotations @{term "A"}, @{term "L"}, @{term "R"} and @{term "W"}. The acquired addresses @{term "A"} must not be owned by any other thread and stem from the shared addresses or are already owned. Reacquiring owned addresses can be used to change the shared-status via the set of local addresses @{term "L"} which have to be a subset of @{term "A"}. The released addresses @{term "R"} have to be owned and distinct from the acquired addresses @{term "A"}. After the write the new ownership set of the thread is obtained by adding the acquired addresses @{term "A"} and releasing the addresses @{term "R"}: @{term "\ \ A - R"}. The released addresses @{term "R"} are augmented to the shared addresses @{term "S"} and the local addresses @{term "L"} are removed. We also take care about the write permissions in the shared state: the released addresses in set @{term "W"} as well as the acquired addresses are marked writable: @{term "\ \\<^bsub>W\<^esub> R \\<^bsub>A\<^esub> L"}. The auxiliary ternary operators to augment and subtract addresses from the sharing map are defined as follows: @{thm [display] augment_shared_def [where S=R, no_vars]} @{thm [display,margin=80] restrict_shared_def [no_vars]} The read-modify-write instruction first adds the current value at address @{term "a"} to temporary @{term "t"} and then checks the store condition @{term "cond"} on the temporaries. If it fails this read is the final result of the operation. Otherwise the store is performed. The resulting value of the temporary @{term "t"} is specified by the function @{term "ret"} which considers both the old and new value as input. As the read-modify-write instruction is an interlocked operation which flushes the store buffer as a side effect the dirty flag @{term "\"} is reset. The other effects on the ghost state and the safety sideconditions are the same as for the volatile read and volatile write, respectively. The only effect of the fence instruction in the system without store buffer is to reset the dirty flag. The ghost instruction @{term "Ghost A L R W"} allows to transfer ownership when no write is involved \ie when merely reading from memory. It has the same safety requirements as the corresponding parts in the write instructions. \ subsection \Reduction \label{sec:reduction}\ text (in xvalid_program_progress) \ The reduction theorem we aim at reduces a computation of a machine with store buffers to a sequential consistent computation of the virtual machine. We formulate this as a simulation theorem which states that a computation of the store buffer machine @{term "(ts\<^sub>s\<^sub>b,m,()) \\<^sub>s\<^sub>b\<^sup>* (ts\<^sub>s\<^sub>b',m',())"} can be simulated by a computation of the virtual machine @{term "(ts,m,\) \\<^sub>v\<^sup>* (ts',m',\')"}. The main theorem only considers computations that start in an initial configuration where all store buffers are empty and end in a configuration where all store buffers are empty again. A configuration of the store buffer machine is obtained from a virtual configuration by removing all ghost components and assuming empty store buffers. This coupling relation between the thread configurations is written as @{term "ts\<^sub>s\<^sub>b \\<^sub>d ts"}. Moreover, the precondition @{term "initial\<^sub>v ts \ valid"} ensures that the ghost state of the initial configuration of the virtual machine is properly initialized: the ownership sets of the threads are distinct, an address marked as read-only (according to @{term \}) is unowned and every unowned address is shared. %TODO (ommit): and the instruction lists are empty. Finally with @{term [names_short] "safe_reach_virtual_free_flowing (ts,m,S)"} we ensure conformance to the programming discipline by assuming that all reachable configuration in the virtual machine are safe (according to the rules in Figure~\ref{fig:safe-virtual-memory}). % \begin{theorem}[Reduction]\label{thm:reduction} @{thm [display, mode=compact, mode=holimplnl, margin=90,names_short] store_buffer_execution_result_sequential_consistent'_hol [where x="()" and x'="()",no_vars]} \end{theorem} % This theorem captures our intiution that every result that can be obtained from a computation of the store buffer machine can also be obtained by a sequentially consistent computation. However, to prove it we need some generalizations that we sketch in the following sections. First of all the theorem is not inductive as we do not consider arbitrary intermediate configurations but only those where all store buffers are empty. For intermediate confiugrations the coupling relation becomes more involved. The major obstacle is that a volatile read (from memory) can overtake non-volatile writes that are still in the store-buffer and have not yet emerged to memory. Keep in mind that our programming discipline only ensures that no \emph{volatile} writes can be in the store buffer the moment we do a volatile read, outstanding non-volatile writes are allowed. This reordering of operations is reflected in the coupling relation for intermediate configurations as discussed in the following section. \ section \Building blocks of the proof \label{sec:buildingblocks}\ text (in program) \ A corner stone of the proof is a proper coupling relation between an \emph{intermediate} configuration of a machine with store buffers and the virtual machine without store buffers. It allows us to simulate every computation step of the store buffer machine by a sequence of steps (potentially empty) on the virtual machine. This transformation is essentially a sequentialization of the trace of the store buffer machine. When a thread of the store buffer machine executes a non-volatile operation, it only accesses memory which is not modified by any other thread (it is either owned or read-only). Although a non-volatile store is buffered, we can immediately execute it on the virtual machine, as there is no competing store of another thread. However, with volatile writes we have to be careful, since concurrent threads may also compete with some volatile write to the same address. At the moment the volatile write enters the store buffer we do not yet know when it will be issued to memory and how it is ordered relatively to other outstanding writes of other threads. We therefore have to suspend the write on the virtual machine from the moment it enters the store buffer to the moment it is issued to memory. For volatile reads our programming discipline guarantees that there is no volatile write in the store buffer by flushing the store buffer if necessary. So there are at most some outstanding non-volatile writes in the store buffer, which are already executed on the virtual machine, as described before. One simple coupling relation one may think of is to suspend the whole store buffer as not yet executed intructions of the virtual machine. However, consider the following scenario. A thread is reading from a volatile address. It can still have non-volatile writes in its store buffer. Hence the read would be suspended in the virutal machine, and other writes to the address (e.g. interlocked or volatile writes of another thread) could invalidate the value. Altogether this suggests the following refined coupling relation: the state of the virtual machine is obtained from the state of the store buffer machine, by executing each store buffer until we reach the first volatile write. The remaining store buffer entries are suspended as instructions. As we only execute non volatile writes the order in which we execute the store buffers should be irrelevant. This coupling relation allows a volatile read to be simulated immediately on the virtual machine as it happens on the store buffer machine. From the viewpoint of the memory the virtual machine is ahead of the store buffer machine, as leading non-volatile writes already took effect on the memory of the virtual machine while they are still pending in the store buffer. However, if there is a volatile write in the store buffer the corresponding thread in the virtual machine is suspended until the write leaves the store buffer. So from the viewpoint of the already executed instructions the store buffer machine is ahead of the virtual machine. To keep track of this delay we introduce a variant of the store buffer machine below, which maintains the history of executed instructions in the store buffer (including reads and program steps). Moreover, the intermediate machine also maintains the ghost state of the virtual machine to support the coupling relation. We also introduce a refined version of the virutal machine below, which we try to motivate now. Esentially the programming discipline only allows races between volatile (or interlocked) operations. By race we mean two competing memory accesses of different threads of which at least one is a write. For example the discipline guarantees that a volatile read may not be invalidated by a non-volatile write of another thread. While proving the simulation theorem this manifests in the argument that a read of the store-buffer machine and the virtual machine sees the same value in both machines: the value seen by a read in the store buffer machine stays valid as long as it has not yet made its way out in the virtual machine. To rule out certain races from the execution traces we make use of the programming discipline, which is formalized in the safety of all reachable configurations of the virtual machine. Some races can be ruled out by continuing the computation of the virtual machine until we reach a safety violation. However, some cannot be ruled out by the future computation of the current trace, but can be invalidated by a safety violation of another trace that deviated from the current one at some point in the past. Consider two threads. Thread 1 attempts to do a volatile read from address @{term a} which is currently owned (and not shared) by thread 2, which attempts to do a non-volatile write on @{term a} with value @{term "42::nat"} and then release the address. In this configuration there is already a safety violation. Thread 1 is not allowed to perform a volatile read from an address that is not shared. However, when Thread 2 has executed his update and has released ownership (both are non-volatile operations) there is no safety violation anymore. Unfortunately this is the state of the virtual machine when we consider the instructions of Thread 2 to be in the store buffer. The store buffer machine and the virtual machine are out of sync. Whereas in the virtual machine Thread 1 will already read @{term "42::nat"} (all non-volatile writes are already executed in the virtual machine), the non-volatile write may still be pending in the store buffer of Thread 2 and hence Thread 1 reads the old value in the store buffer machine. This kind of issues arise when a thread has released ownership in the middle of non-volatile operations of the virtual machine, but the next volatile write of this thread has not yet made its way out of the store buffer. When another thread races for the released address in this situation there is always another scheduling of the virtual machine where the release has not yet taken place and we get a safety violation. To make these safety violations visible until the next volatile write we introduce another ghost component that keeps track of the released addresses. It is augmented when an ghost operation releases an address and is reset as the next volatile write is reached. Moreover, we refine our rules for safety to take these released addresses into account. For example, a write to an released address of another thread is forbidden. We refer to these refined model as \emph{delayed releases} (as no other thread can acquire the address as long as it is still in the set of released addresses) and to our original model as \emph{free flowing releases} (as the effect of a release immediate takes place at the point of the ghost instruction). Note that this only affects ownership transfer due to the @{term Ghost} instruction. Ownership transfer together with volatile (or interlocked) writes happen simultaneously in both models. Note that the refined rules for delayed releases are just an intermediate step in our proof. They do not have to be considered for the final programming discipline. As sketched above we can show in a separate theorem that a safety violation in a trace with respect to delayed releases implies a safety violation of a (potenitally other) trace with respect to free flowing releases. Both notions of safety collaps in all configurations where there are no released addresses, like the initial state. So if all reachable configurations are safe with respect to free flowing releases they are also safe with respect to delayed releases. This allows us to use the stricter policy of delayed releases for the simulation proof. Before continuing with the coupling relation, we introduce the refined intermediate models for delayed releases and store buffers with history information. \ subsection \Intermediate models\ text (in program) \ We begin with the virtual machine with delayed releases, for which the memory transitions @{term "(is,\,(),m,\,\,\,\) \ (is',\',(),m',\',\',\',\')"} are defined Figure \ref{fig:virtual-delayed-memory}. % \begin{figure} \begin{center} @{thm [mode=Axiom] DRead [where x="()",no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] DWriteNonVolatile [where x="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule,names_short] DWriteVolatile [where x="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule,names_short] DRMWReadOnly [where x="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule,names_short] DRMWWrite [where x="()",no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom,names_short] direct_memop_step.Fence [where x="()",no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] DGhost [where x="()",no_vars]}\\[0.1\baselineskip] \end{center} \caption{Memory transitions of the virtual machine with delayed releases\label{fig:virtual-delayed-memory}} \end{figure} % The additional ghost component @{term "\"} is a mapping from addresses to a Boolean flag. If an address is in the domain of @{term \} it was released. The boolean flag is considered to figure out if the released address was previously shared or not. In case the flag is @{term True} it was shared otherwise not. This subtle distinction is necessary to properly handle volatile reads. A volatile read from an address owned by another thread is fine as long as it is marked as shared. The released addresses @{term \} are reset at every volatile write as well as interlocked operations and the fence instruction. They are augmented at the ghost instruction taking the sharing information into account: @{thm [display] augment_rels_def [where S="dom \", no_vars]} If an address is freshly released (@{term "a \ R"} and @{term "\ a = None"}) the flag is set according to @{term "dom \"}. Otherwise the flag becomes @{term "Some False"} in case the released address is currently unshared. Note that with this definition @{term "\ a = Some False"} stays stable upon every new release and we do not loose information about a release of an unshared address. The global transition @{term "(ts, m, s) \\<^sub>d (ts',m',s')"} are analogous to the rules in Figure \ref{fig:global-virtual-step} replacing the memory transtions with the refined version for delayed releases. The safety judgment for delayed releases @{term "\s,\s,i\ (is, \, m, \, \, \)\"} is defined in Figure \ref{fig:safe-delayed}. Note the additional component @{term \s} which is the list of release maps of all threads. % \begin{figure} \begin{center} @{thm [mode=Rule] safe_delayed_direct_memop_state.Read [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] safe_delayed_direct_memop_state.WriteNonVolatile [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SafeDelayedWriteVolatile [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SafeDelayedRMWReadOnly [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SafeDelayedRMWWrite [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] safe_delayed_direct_memop_state.Fence [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] safe_delayed_direct_memop_state.Ghost [no_vars]}\\[0.1\baselineskip] @{thm [mode=Rule] safe_delayed_direct_memop_state.Nil [no_vars]}\\[0.1\baselineskip] \end{center} \caption{Safe configurations of a virtual machine (delayed-releases) \label{fig:safe-delayed}} \end{figure} % The rules are strict extensions of the rules in Figure \ref{fig:safe-virtual-memory}: writing or acquiring an address @{term a} is only allowed if the address is not in the release set of another thread (@{term "a \ dom (\s!j)"}); reading from an address is only allowed if it is not released by another thread while it was unshared (@{term "(\s!j) a \ Some False"}). For the store buffer machine with history information we not only put writes into the store buffer but also record reads, program steps and ghost operations. This allows us to restore the necessary computation history of the store buffer machine and relate it to the virtual machine which may fall behind the store buffer machine during execution. Altogether an entry in the store buffer is either a \begin{itemize} \item @{term "Read\<^sub>s\<^sub>b volatile a t v"}, recording a corresponding read from address @{term "a"} which loaded the value @{term "v"} to temporary @{term "t"}, or a \item @{term "Write\<^sub>s\<^sub>b volatile a sop v"} for an outstanding write, where operation @{term "sop"} evaluated to value @{term "v"}, or of the form \item @{term "Prog\<^sub>s\<^sub>b p p' is'"}, recording a program transition from @{term "p"} to @{term "p'"} which issued instructions @{term "is'"}, or of the form \item @{term "Ghost\<^sub>s\<^sub>b A L R W"}, recording a corresponding ghost operation. \end{itemize} As defined in Figure \ref{fig:store-buffer-transitions} a write updates the memory when it exits the store buffer, all other store buffer entries may only have an effect on the ghost state. The effect on the ownership information is analogous to the corresponding operations in the virtual machine. % \begin{figure} \begin{center} @{thm [mode=Axiom] Write\<^sub>s\<^sub>bNonVolatile [where rs=sb, no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule, names_short] Write\<^sub>s\<^sub>bVolatile [where rs=sb, no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] flush_step.Read\<^sub>s\<^sub>b [where rs=sb, no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] flush_step.Prog\<^sub>s\<^sub>b [where rs=sb, no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] Ghost\<^sub>s\<^sub>b [where rs=sb, no_vars]} \end{center} \caption{Store buffer transitions with history\label{fig:store-buffer-transitions}} \end{figure} % The memory transitions defined in Figure \ref{fig:store-buffer-history-memory} are straightforward extensions of the store buffer transitions of Figure \ref{fig:store-buffer-history-memory} augmented with ghost state and recording history information in the store buffer. Note how we deal with the ghost state. Only the dirty flag is updated when the instruction enters the store buffer, the ownership transfer takes effect when the instruction leaves the store buffer. % \begin{figure} \begin{center} @{thm [mode=Rule] SBHRead [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SBHWriteNonVolatile' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] SBHWriteVolatile' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule,names_short] SBHRMWReadOnly' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule,names_short] SBHRMWWrite' [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom,names_short] sbh_memop_step.SBHFence [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] SBHGhost' [no_vars]} \end{center} \caption{Memory transitions of store buffer machine with history\label{fig:store-buffer-history-memory}} \end{figure} % The global transitions @{term "(ts\<^sub>s\<^sub>b\<^sub>h, m, \) \\<^sub>s\<^sub>b\<^sub>h (ts\<^sub>s\<^sub>b\<^sub>h',m',\')"} are analogous to the rules in Figure \ref{fig:global-transitions} replacing the memory transtions and store buffer transtiontions accordingly. \ subsection \Coupling relation \label{sec:couplingrelation}\ text (in program) \ After this introduction of the immediate models we can proceed to the details of the coupling relation, which relates configurations of the store buffer machine with histroy and the virtual machine with delayed releases. Remember the basic idea of the coupling relation: the state of the virtual machine is obtained from the state of the store buffer machine, by executing each store buffer until we reach the first volatile write. The remaining store buffer entries are suspended as instructions. The instructions now also include the history entries for reads, program steps and ghost operations. The suspended reads are not yet visible in the temporaries of the virtual machine. Similar the ownership effects (and program steps) of the suspended operations are not yet visible in the virtual machine. The coupling relation between the store buffer machine and the virtual machine is illustrated in Figure~\ref{fig:coupling-relation-pic}. The threads issue instructions to the store buffers from the right and the instructions emerge from the store buffers to main memory from the left. The dotted line illustrates the state of the virtual machines memory. It is obtained from the memory of the store buffer machine by executing the purely non-volatile prefixes of the store buffers. The remaining entries of the store buffer are still (suspended) instructions in the virtual machine. \begin{figure} \centering \begin{tikzpicture} \tikzstyle{sbnodel}=[shape=rectangle, draw=black, text badly centered, outer sep=0cm] \tikzstyle{sbnoder}=[shape=rectangle, draw=black, text ragged, outer sep=0cm] \tikzstyle{nonvolatile}=[fill=gray!10] \tikzstyle{virtual}=[dotted] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Store buffers and instructions % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \node (sbl0) [style=sbnodel,style=nonvolatile,text width=1.3cm] {nv}; \node (sbr0) [style=sbnoder,text width=1.7cm, right] at(sbl0.east) {v}; \node (ins0) [right] at ($ (sbr0.east) $) {thread $0$: $i_0^0$, $i_0^1$, $\dots$}; \node (sbl1) [style=sbnodel,style=nonvolatile,text width=2.0cm,below right] at ($ (sbl0.south west) -(0,0.6cm) $) {nv}; \node (sbr1) [style=sbnoder,text width=1.0cm, right] at(sbl1.east) {v}; \node (ins1) [right] at ($ (sbr1.east) $) {thread $i$: $i_i^0$, $i_i^1$, $\dots$}; \node (sbl2) [style=sbnodel,style=nonvolatile,text width=1.7cm,below right] at ($ (sbl1.south west) -(0,0.6cm) $) {nv}; \node (sbr2) [style=sbnoder,text width=1.3cm, right] at(sbl2.east) {v}; \node (ins2) [right] at ($ (sbr2.east) $) {thread $j$: $i_j^0$, $i_j^1$, $\dots$}; \node (sbl3) [style=sbnodel,style=nonvolatile,text width=1.4cm,below right] at ($ (sbl2.south west) -(0,0.6cm) $) {nv}; \node (sbr3) [style=sbnoder,text width=1.6cm, right] at(sbl3.east) {v}; \node (ins3) [right] at ($ (sbr3.east) $) {thread $n$: $i_n^0$, $i_n^1$, $\dots$}; \path (sbr1.north east) to node [near end,left] {$\vdots$} (sbr0.south east); \path (sbr2.north east) to node [near end,left] {$\vdots$} (sbr1.south east); \path (sbr3.north east) to node [near end,left] {$\vdots$} (sbr2.south east); \node (sblabel)[above] at ($ (sbr0.north west) +(0,0.6cm)$) {$\leftarrow$ store buffers}; \node (inslabel)[above] at ($ (sbr0.north east) +(1.5cm,0.6cm)$) {$\leftarrow$ instructions}; %%%%%%%%%% % Memory % %%%%%%%%%% \coordinate (memNorthWest) at ($ (sbl0.north west) -(2.0cm,0cm) $); \coordinate (memSouthEast) at ($ (sbl3.south west) -(0.5cm,0cm) $); \filldraw[style=nonvolatile,rounded corners] (memNorthWest) rectangle (memSouthEast) node [midway] {@{term "m\<^sub>s\<^sub>b\<^sub>h"}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Virtual memory boundaries % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% \draw [style=virtual,out=90,in=-90](sbr1.north west) to (sbr0.south west); \draw [style=virtual,out=90,in=-90](sbr2.north west) to (sbr1.south west); \draw [style=virtual,out=90,in=-90](sbr3.north west) to (sbr2.south west); \draw [style=virtual] ($ (memNorthWest) +(0,0.4cm) $) to ($ (sbr0.north west) + (-0.3,0.4)$)[out=0,in=90] to ($ (sbr0.north west)$); \draw [style=virtual]($ (memNorthWest |- memSouthEast) +(0,-0.8cm) $) to node[midway,above]{@{term m}} ($ (sbr3.south west) + (-0.3,-0.8)$)[out=0,in=90] to ($ (sbr3.south west)$); \node (execslabel)[below] at ($ (sbl3.south)$) {executed}; \node (suspendslabel)[below] at ($ (sbr3.south)$) {suspended}; \end{tikzpicture} \caption{Illustration of coupling relation \label{fig:coupling-relation-pic}} \end{figure} Consider the following configuration of a thread @{term "ts\<^sub>s\<^sub>b\<^sub>h ! j"} in the store buffer machine, where @{term "i\<^sub>k"} are the instructions and @{term "s\<^sub>k"} the store buffer entries. Let @{term "s\<^sub>v"} be the first volatile write in the store buffer. Keep in mind that new store buffer entries are appended to the end of the list and entries exit the store buffer and are issued to memory from the front of the list. % \begin{center} @{term "ts\<^sub>s\<^sub>b\<^sub>h ! j = (p,[i\<^sub>1,\,i\<^sub>n], \, [s\<^sub>1,\,s\<^sub>v,s\<^sub>\,\,s\<^sub>m], \, \, \)"} \end{center} % The corresponding configuration @{term "ts ! j"} in the virtual machine is obtained by suspending all store buffer entries beginning at @{term "s\<^sub>v"} to the front of the instructions. A store buffer @{term "Read\<^sub>s\<^sub>b"} / @{term "Write\<^sub>s\<^sub>b"} / @{term "Ghost\<^sub>s\<^sub>b"} is converted to a @{term "Read"} / @{term "Write"} / @{term "Ghost"} instruction. We take the freedom to make this coercion implicit in the example. The store buffer entries preceding @{term "s\<^sub>v"} have already made their way to memory, whereas the suspended read operations are not yet visible in the temporaries @{term "\'"}. Similar, the suspended updates to the ownership sets and dirty flag are not yet recorded in @{term "\'"}, @{term "\'"} and @{term "\'"}. % \begin{center} @{term "ts ! j =(p,[s\<^sub>v,s\<^sub>\,\,s\<^sub>m,i\<^sub>1,\,i\<^sub>n], \', (), \', \',\')"} \end{center} % This example illustrates that the virtual machine falls behind the store buffer machine in our simulation, as store buffer instructions are suspended and reads (and ghost operations) are delayed and not yet visible in the temporaries (and the ghost state). This delay can also propagate to the level of the programming language, which communicates with the memory system by reading the temporaries and issuing new instructions. For example the control flow can depend on the temporaries, which store the result of branching conditions. It may happen that the store buffer machine already has evaluated the branching condition by referring to the values in the store buffer, whereas the virtual machine still has to wait. Formally this manifests in still undefined temporaries. Now consider that the program in the store buffer machine makes a step from @{term "p"} to @{term "(p',is')"}, which results in a thread configuration where the program state has switched to @{term "p'"}, the instructions @{term "is'"} are appended and the program step is recorded in the store buffer: % \begin{center} @{term "ts\<^sub>s\<^sub>b\<^sub>h' ! j = (p',[i\<^sub>1,\,i\<^sub>n]@is', \, [s\<^sub>1,\,s\<^sub>v,\,s\<^sub>m,Prog\<^sub>s\<^sub>b p p' is'], \, \, \)"} \end{center} % The virtual machine however makes no step, since it still has to evaluate the suspended instructions before making the program step. The instructions @{term "is'"} are not yet issued and the program state is still @{term "p"}. We also take these program steps into account in our final coupling relation @{thm (concl) sim_config' [no_vars]}, defined in Figure~\ref{fig:coupling-relation}. % \begin{figure} \begin{center} \begin{minipage}{10cm} \inferrule{@{thm (prem 1) sim_config' [no_vars]}\\ @{thm (prem 2) sim_config' [no_vars]}\\ @{thm (prem 3) sim_config' [no_vars]}\\ \parbox{9.8cm}{@{thm [break,mode=letnl,margin=80] (prem 4) sim_config' [simplified restrict_map_inverse, no_vars]}}} % {@{thm (concl) sim_config' [no_vars]}} \end{minipage} \end{center} \caption{Coupling relation \label{fig:coupling-relation}} \end{figure} % We denote the already simulated store buffer entries by @{term "Bind execs. execs"} and the suspended ones by @{term "Bind suspends. suspends"}. The function @{term "instrs"} converts them back to instructions, which are a prefix of the instructions of the virtual machine. We collect the additional instructions which were issued by program instructions but still recorded in the remainder of the store buffer with function @{term "prog_instrs"}. These instructions have already made their way to the instructions of the store buffer machine but not yet on the virtual machine. This situation is formalized as @{term "Bind suspends is\<^sub>s\<^sub>b\<^sub>h is. instrs suspends @ is\<^sub>s\<^sub>b\<^sub>h = is @ prog_instrs suspends"}, where @{term "Bind is. is"} are the instructions of the virtual machine. The program state of the virtual machine is either the same as in the store buffer machine or the first program state recorded in the suspended part of the store buffer. This state is selected by @{const "hd_prog"}. The temporaries of the virtual machine are obtained by removing the suspended reads from @{term "\"}. The memory is obtained by executing all store buffers until the first volatile write is hit, excluding it. Thereby only non-volatile writes are executed, which are all thread local, and hence could be executed in any order with the same result on the memory. Function @{const "flush_all_until_volatile_write"} executes them in order of appearance. Similarly the sharing map of the virtual machine is obtained by executing all store buffers until the first volatile write via the function @{const "share_all_until_volatile_write"}. For the local ownership set @{term "\\<^sub>s\<^sub>b\<^sub>h"} the auxiliary function @{term "acquire"} calculates the outstanding effect of the already simulated parts of the store buffer. Analogously @{term "release"} calculates the effect for the released addresses @{term "\\<^sub>s\<^sub>b\<^sub>h"}. \ subsection \Simulation \label{sec:simulation}\ text (in xvalid_program_progress) \ Theorem \ref{thm:simulation} is our core inductive simulation theorem. Provided that all reachable states of the virtual machine (with delayed releases) are safe, a step of the store buffer machine (with history) can be simulated by a (potentially empty) sequence of steps on the virtual machine, maintaining the coupling relation and an invariant on the configurations of the store buffer machine. % \begin{theorem}[Simulation]\label{thm:simulation} @{thm [display, mode=holimplnl,margin=100] simulation_hol [no_vars]} \end{theorem} % In the following we discuss the invariant @{term [names_short] "invariant ts\<^sub>s\<^sub>b\<^sub>h S\<^sub>s\<^sub>b\<^sub>h m\<^sub>s\<^sub>b\<^sub>h"}, where we commonly refer to a thread configuration @{term "ts\<^sub>s\<^sub>b\<^sub>h!i = (p,is,\,sb,\,\,\)"} for @{term "i < length ts\<^sub>s\<^sub>b\<^sub>h"}. By outstanding references we refer to read and write operations in the store buffer. The invariant is a conjunction of several sub-invariants grouped by their content: @{thm [display, names_short, mode=compact, margin=100] invariant_grouped_def [of ts\<^sub>s\<^sub>b\<^sub>h S\<^sub>s\<^sub>b\<^sub>h m\<^sub>s\<^sub>b\<^sub>h]} %TODO make grouping formally, hide program step in valid_history \paragraph{Ownership.} \begin{inparaenum} \item \label{inv-ownership:owned-or-read-only} For every thread all outstanding non-volatile references have to be owned or refer to read-only memory. \item Every outstanding volatile write is not owned by any other thread. \item Outstanding accesses to read-only memory are not owned. \item \label{inv-ownership:distinct-ownership} The ownership sets of every two different threads are distinct. \end{inparaenum} \paragraph{Sharing.} \begin{inparaenum} \item \label{inv-sharing:non-volatile-writes-unshared} All outstanding non volatile writes are unshared. \item All unowned addresses are shared. \item No thread owns read-only memory. \item The ownership annotations of outstanding ghost and write operations are consistent (\eg released addresses are owned at the point of release). \item \label{inv-sharing:no-write-to-read-only-memory} There is no outstanding write to read-only memory. \end{inparaenum} \paragraph{Temporaries.} Temporaries are modeled as an unlimited store for temporary registers. We require certain distinctness and freshness properties for each thread. \begin{inparaenum} \item The temporaries referred to by read instructions are distinct. \item The temporaries referred to by reads in the store buffer are distinct. \item Read and write temporaries are distinct. \item Read temporaries are fresh, \ie are not in the domain of @{term "\"}. \end{inparaenum} \paragraph{Data dependency.} Data dependency means that store operations may only depend on \emph{previous} read operations. For every thread we have: \begin{inparaenum} \item Every operation @{term "(D, f)"} in a write instruction or a store buffer write is valid according to @{term "valid_sop (D, f)"}, \ie function @{term "f"} only depends on domain @{term "D"}. \item For every suffix of the instructions of the form @{term "Write volatile a (D,f) A L R W#is"} the domain @{term "D"} is distinct from the temporaries referred to by future read instructions in @{term "is"}. \item The outstanding writes in the store buffer do not depend on the read temporaries still in the instruction list. \end{inparaenum} \paragraph{History.} The history information of program steps and read operations we record in the store buffer have to be consistent with the trace. For every thread: \begin{inparaenum} \item The value stored for a non volatile read is the same as the last write to the same address in the store buffer or the value in memory, in case there is no write in the buffer. \item All reads have to be clean. This results from our flushing policy. Note that the value recorded for a volatile read in the initial part of the store buffer (before the first volatile write), may become stale with respect to the memory. Remember that those parts of the store buffer are already executed in the virtual machine and thus cause no trouble. \item For every read the recorded value coincides with the corresponding value in the temporaries. \item For every @{term "Write\<^sub>s\<^sub>b volatile a (D,f) v A L R W"} the recorded value @{term "v"} coincides with @{term "f \"}, and domain @{term "D"} is subset of @{term "dom \"} and is distinct from the following read temporaries. Note that the consistency of the ownership annotations is already covered by the aforementioned invariants. \item For every suffix in the store buffer of the form @{term "Prog\<^sub>s\<^sub>b p\<^sub>1 p\<^sub>2 is'#sb'"}, either @{term "p\<^sub>1 = p"} in case there is no preceding program node in the buffer or it corresponds to the last program state recorded there. Moreover, the program transition @{term "\|`(- read_tmps sb')\ p\<^sub>1 \\<^sub>p (p\<^sub>2,is')"} is possible, \ie it was possible to execute the program transition at that point. \item The program configuration @{term "p"} coincides with the last program configuration recorded in the store buffer. \item As the instructions from a program step are at the one hand appended to the instruction list and on the other hand recorded in the store buffer, we have for every suffix @{term "sb'"} of the store buffer: @{term "\is'. instrs sb' @ is = is' @ prog_instrs sb'"}, \ie the remaining instructions @{term "is"} correspond to a suffix of the recorded instructions @{term "prog_instrs sb'"}. \end{inparaenum} \paragraph{Flushes.} If the dirty flag is unset there are no outstanding volatile writes in the store buffer. \paragraph{Program step.} The program-transitions are still a parameter of our model. In order to make the proof work, we have to assume some of the invariants also for the program steps. We allow the program-transitions to employ further invariants on the configurations, these are modeled by the parameter @{term "valid"}. For example, in the instantiation later on the program keeps a counter for the temporaries, for each thread. We maintain distinctness of temporaries by restricting all temporaries occurring in the memory system to be below that counter, which is expressed by instantiating @{term "valid"}. Program steps, memory steps and store buffer steps have to maintain @{term "valid"}. Furthermore we assume the following properties of a program step: \begin{inparaenum} \item The program step generates fresh, distinct read temporaries, that are neither in @{term "\"} nor in the store buffer temporaries of the memory system. \item The generated memory instructions respect data dependencies, and are valid according to @{term "valid_sop"}. %TODO: maybe we can omit the formal stuff, intuition should be clear, depends on what we write on PIMP. \end{inparaenum} \paragraph{Proof sketch.} We do not go into details but rather first sketch the main arguments for simulation of a step in the store buffer machine by a potentially empty sequence of steps in the virtual machine, maintaining the coupling relation. Second we exemplarically focus on some cases to illustrate common arguments in the proof. The first case distinction in the proof is on the global transitions in Figure~\ref{fig:global-transitions}. % \begin{inparaenum} \item \emph{Program step}: we make a case distinction whether there is an outstanding volatile write in the store buffer or not. If not the configuration of the virtual machine corresponds to the executed store buffer and we can make the same step. Otherwise the virtual machine makes no step as we have to wait until all volatile writes have exited the store buffer. % \item \emph{Memory step}: we do case distinction on the rules in Figure~\ref{fig:store-buffer-history-memory}. For read, non volatile write and ghost instructions we do the same case distinction as for the program step. If there is no outstanding volatile write in the store buffer we can make the step, otherwise we have to wait. When a volatile write enters the store buffer it is suspended until it exists the store buffer. Hence we do no step in the virtual machine. The read-modify-write and the fence instruction can all be simulated immediately since the store buffer has to be empty. % \item \emph{Store Buffer step}: we do case distinction on the rules in Figure~\ref{fig:store-buffer-transitions}. When a read, a non volatile write, a ghost operation or a program history node exits the store buffer, the virtual machine does not have to do any step since these steps are already visible. When a volatile write exits the store buffer, we execute all the suspended operations (including reads, ghost operations and program steps) until the next suspended volatile write is hit. This is possible since all writes are non volatile and thus memory modifications are thread local. \end{inparaenum} In the following we exemplarically describe some cases in more detail to give an impression on the typical arguments in the proof. We start with a configuration @{term "c\<^sub>s\<^sub>b\<^sub>h=(ts\<^sub>s\<^sub>b\<^sub>h,m\<^sub>s\<^sub>b\<^sub>h,\\<^sub>s\<^sub>b\<^sub>h)"} of the store buffer machine, where the next instruction to be executed is a read of thread @{term i}: @{term "Read\<^sub>s\<^sub>b volatile a t"}. The configuration of the virtual machine is @{term "cfg=(ts,m,\)"}. We have to simulate this step on the virtual machine and can make use of the coupling relations @{term "(ts\<^sub>s\<^sub>b\<^sub>h,m\<^sub>s\<^sub>b\<^sub>h,\\<^sub>s\<^sub>b\<^sub>h) \ (ts,m,\)"}, the invariants @{term "invariant ts\<^sub>s\<^sub>b\<^sub>h \\<^sub>s\<^sub>b\<^sub>h m\<^sub>s\<^sub>b\<^sub>h"} and the safety of all reachable states of the virtual machine: @{term "safe_reach_direct_delayed (ts,m,\)"}. The state of the store buffer machine and the coupling with the volatile machine is depicted in Figure~\ref{fig:coupling-i-read}. Note that if there are some suspended instructions in thread @{term i}, we cannot directly exploit the 'safety of the read', as the virtual machine has not yet reached the state where thread @{term i} is poised to do the read. But fortunately we have safety of the virtual machien of all reachable states. Hence we can just execute all suspended instructions of thread @{term i} until we reach the read. We refer to this configuration of the virtual machine as @{term "cfg''=(ts'',m'',\'')"}, which is depicted in Figure~\ref{fig:coupling-i-read-forward}. \begin{figure} \centering \begin{tikzpicture} \tikzstyle{sbnodel}=[shape=rectangle, draw=black, text badly centered, outer sep=0cm] \tikzstyle{sbnoder}=[shape=rectangle, draw=black, text ragged, outer sep=0cm] \tikzstyle{nonvolatile}=[fill=gray!10] \tikzstyle{virtual}=[dotted] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Store buffers and instructions % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \node (sbl0) [style=sbnodel,style=nonvolatile,text width=1.3cm] {nv}; \node (sbr0) [style=sbnoder,text width=1.7cm, right] at(sbl0.east) {v}; \node (ins0) [right] at ($ (sbr0.east) $) {thread $0$: $i_0^0$, $i_0^1$, $\dots$}; \node (sbl1) [style=sbnodel,style=nonvolatile,text width=2.0cm,below right] at ($ (sbl0.south west) -(0,0.6cm) $) {nv}; \node (sbr1) [style=sbnoder,text width=1.0cm, right] at(sbl1.east) {v}; \node (ins1) [right] at ($ (sbr1.east) $) {thread $i$: @{term "Read\<^sub>s\<^sub>b volatile a t"},$\dots$}; \node (sbl2) [style=sbnodel,style=nonvolatile,text width=1.7cm,below right] at ($ (sbl1.south west) -(0,0.6cm) $) {nv}; \node (sbr2) [style=sbnoder,text width=1.3cm, right] at(sbl2.east) {v}; \node (ins2) [right] at ($ (sbr2.east) $) {thread $j$: $i_j^0$, $i_j^1$, $\dots$}; \node (sbl3) [style=sbnodel,style=nonvolatile,text width=1.4cm,below right] at ($ (sbl2.south west) -(0,0.6cm) $) {nv}; \node (sbr3) [style=sbnoder,text width=1.6cm, right] at(sbl3.east) {v}; \node (ins3) [right] at ($ (sbr3.east) $) {thread $n$: $i_n^0$, $i_n^1$, $\dots$}; \path (sbr1.north east) to node [near end,left] {$\vdots$} (sbr0.south east); \path (sbr2.north east) to node [near end,left] {$\vdots$} (sbr1.south east); \path (sbr3.north east) to node [near end,left] {$\vdots$} (sbr2.south east); \node (sblabel)[above] at ($ (sbr0.north west) +(0,0.6cm)$) {$\leftarrow$ store buffers}; \node (inslabel)[above] at ($ (sbr0.north east) +(1.5cm,0.6cm)$) {$\leftarrow$ instructions}; %%%%%%%%%% % Memory % %%%%%%%%%% \coordinate (memNorthWest) at ($ (sbl0.north west) -(2.0cm,0cm) $); \coordinate (memSouthEast) at ($ (sbl3.south west) -(0.5cm,0cm) $); \filldraw[style=nonvolatile,rounded corners] (memNorthWest) rectangle (memSouthEast) node [midway] {@{term "m\<^sub>s\<^sub>b\<^sub>h"}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Virtual memory boundaries % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% \draw [style=virtual,out=90,in=-90](sbr1.north west) to (sbr0.south west); \draw [style=virtual,out=90,in=-90](sbr2.north west) to (sbr1.south west); \draw [style=virtual,out=90,in=-90](sbr3.north west) to (sbr2.south west); \draw [style=virtual] ($ (memNorthWest) +(0,0.4cm) $) to ($ (sbr0.north west) + (-0.3,0.4)$)[out=0,in=90] to ($ (sbr0.north west)$); \draw [style=virtual]($ (memNorthWest |- memSouthEast) +(0,-0.8cm) $) to node[midway,above]{@{term m}} ($ (sbr3.south west) + (-0.3,-0.8)$)[out=0,in=90] to ($ (sbr3.south west)$); \node (execslabel)[below] at ($ (sbl3.south)$) {executed}; \node (suspendslabel)[below] at ($ (sbr3.south)$) {suspended}; \end{tikzpicture} \caption{Thread @{term i} poised to read \label{fig:coupling-i-read}} \end{figure} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{figure} \centering \begin{tikzpicture} \tikzstyle{sbnodel}=[shape=rectangle, draw=black, text badly centered, outer sep=0cm] \tikzstyle{sbnoder}=[shape=rectangle, draw=black, text ragged, outer sep=0cm] \tikzstyle{nonvolatile}=[fill=gray!10]%todo rename to executed? \tikzstyle{virtual}=[dotted] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Store buffers and instructions % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \node (sbl0) [style=sbnodel,style=nonvolatile,text width=1.3cm] {nv}; \node (sbr0) [style=sbnoder,text width=1.7cm, right] at(sbl0.east) {v}; \node (ins0) [right] at ($ (sbr0.east) $) {thread $0$: $i_0^0$, $i_0^1$, $\dots$}; \node (sbl1) [style=sbnodel,style=nonvolatile,text width=2.0cm,below right] at ($ (sbl0.south west) -(0,0.6cm) $) {nv}; \node (sbr1) [style=sbnoder,style=nonvolatile,text width=1.0cm, right] at(sbl1.east) {v}; \node (ins1) [right] at ($ (sbr1.east) $) {thread $i$: @{term "Read\<^sub>s\<^sub>b volatile a t"},$\dots$}; \node (sbl2) [style=sbnodel,style=nonvolatile,text width=1.7cm,below right] at ($ (sbl1.south west) -(0,0.6cm) $) {nv}; \node (sbr2) [style=sbnoder,text width=1.3cm, right] at(sbl2.east) {v}; \node (ins2) [right] at ($ (sbr2.east) $) {thread $j$: $i_j^0$, $i_j^1$, $\dots$}; \node (sbl3) [style=sbnodel,style=nonvolatile,text width=1.4cm,below right] at ($ (sbl2.south west) -(0,0.6cm) $) {nv}; \node (sbr3) [style=sbnoder,text width=1.6cm, right] at(sbl3.east) {v}; \node (ins3) [right] at ($ (sbr3.east) $) {thread $n$: $i_n^0$, $i_n^1$, $\dots$}; \path (sbr1.north east) to node [near end,left] {$\vdots$} (sbr0.south east); \path (sbr2.north east) to node [near end,left] {$\vdots$} (sbr1.south east); \path (sbr3.north east) to node [near end,left] {$\vdots$} (sbr2.south east); \node (sblabel)[above] at ($ (sbr0.north west) +(0,0.6cm)$) {$\leftarrow$ store buffers}; \node (inslabel)[above] at ($ (sbr0.north east) +(1.5cm,0.6cm)$) {$\leftarrow$ instructions}; %%%%%%%%%% % Memory % %%%%%%%%%% \coordinate (memNorthWest) at ($ (sbl0.north west) -(2.0cm,0cm) $); \coordinate (memSouthEast) at ($ (sbl3.south west) -(0.5cm,0cm) $); \filldraw[style=nonvolatile,rounded corners] (memNorthWest) rectangle (memSouthEast) node [midway] {@{term "m\<^sub>s\<^sub>b\<^sub>h"}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Virtual memory boundaries % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% \draw [style=virtual,out=90,in=-90](sbr1.north east) to (sbr0.south west); \draw [style=virtual,out=90,in=-90](sbr2.north west) to (sbr1.south east); \draw [style=virtual,out=90,in=-90](sbr3.north west) to (sbr2.south west); \draw [style=virtual] ($ (memNorthWest) +(0,0.4cm) $) to ($ (sbr0.north west) + (-0.3,0.4)$)[out=0,in=90] to ($ (sbr0.north west)$); \draw [style=virtual]($ (memNorthWest |- memSouthEast) +(0,-0.8cm) $) to node[midway,above]{@{term m}} ($ (sbr3.south west) + (-0.3,-0.8)$)[out=0,in=90] to ($ (sbr3.south west)$); \node (execslabel)[below] at ($ (sbl3.south)$) {executed}; \node (suspendslabel)[below] at ($ (sbr3.south)$) {suspended}; \end{tikzpicture} \caption{Forwarded computation of virtual machine \label{fig:coupling-i-read-forward}} \end{figure} For now we want to consider the case where the read goes to memory and is not forwarded from the store buffer. The value read is @{term "v=m\<^sub>s\<^sub>b\<^sub>h a"}. Moreover, we make a case distinction wheter there is an outstanding volatile write in the store buffer of thread @{term i} or not. This determines if there are suspended instructions in the virtual machine or not. We start with the case where there is no such write. This means that there are no suspended instructions in thread @{term i} and therefore @{term "cfg''=cfg"}. We have to show that the virtual machine reads the same value from memory: @{term "v = m a"}. So what can go wrong? When can the the memory of the virtual machine hold a different value? The memory of the virtual machine is obtained from the memory of the store buffer machine by executing all store buffers until we hit the first volatile write. So if there is a discrepancy in the value this has to come from a non-volatile write in the executed parts of another thread, let us say thread @{term j}. This write is marked as x in Figure~\ref{fig:coupling-i-read-conflict}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{figure} \centering \begin{tikzpicture} \tikzstyle{sbnodel}=[shape=rectangle, draw=black, text badly centered, outer sep=0cm] \tikzstyle{sbnoder}=[shape=rectangle, draw=black, text ragged, outer sep=0cm] \tikzstyle{nonvolatile}=[fill=gray!10]%todo rename to executed? \tikzstyle{virtual}=[dotted] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Store buffers and instructions % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \node (sbl0) [style=sbnodel,style=nonvolatile,text width=1.3cm] {nv}; \node (sbr0) [style=sbnoder,text width=1.7cm, right] at(sbl0.east) {v}; \node (ins0) [right] at ($ (sbr0.east) $) {thread $0$: $i_0^0$, $i_0^1$, $\dots$}; \node (sbl1) [style=sbnodel,style=nonvolatile,text width=2.0cm,below right] at ($ (sbl0.south west) -(0,0.6cm) $) {nv}; \node (sbr1) [style=sbnoder,style=nonvolatile,text width=1.0cm, right] at(sbl1.east) {v}; \node (ins1) [right] at ($ (sbr1.east) $) {thread $i$: @{term "Read\<^sub>s\<^sub>b volatile a t"},$\dots$}; \node (sbl2) [style=sbnodel,style=nonvolatile,text width=1.7cm,below right] at ($ (sbl1.south west) -(0,0.6cm) $) {x}; \node (sbr2) [style=sbnoder,text width=1.3cm, right] at(sbl2.east) {v}; \node (ins2) [right] at ($ (sbr2.east) $) {thread $j$: $i_j^0$, $i_j^1$, $\dots$}; \node (sbl3) [style=sbnodel,style=nonvolatile,text width=1.4cm,below right] at ($ (sbl2.south west) -(0,0.6cm) $) {nv}; \node (sbr3) [style=sbnoder,text width=1.6cm, right] at(sbl3.east) {v}; \node (ins3) [right] at ($ (sbr3.east) $) {thread $n$: $i_n^0$, $i_n^1$, $\dots$}; \path (sbr1.north east) to node [near end,left] {$\vdots$} (sbr0.south east); \path (sbr2.north east) to node [near end,left] {$\vdots$} (sbr1.south east); \path (sbr3.north east) to node [near end,left] {$\vdots$} (sbr2.south east); \node (sblabel)[above] at ($ (sbr0.north west) +(0,0.6cm)$) {$\leftarrow$ store buffers}; \node (inslabel)[above] at ($ (sbr0.north east) +(1.5cm,0.6cm)$) {$\leftarrow$ instructions}; %%%%%%%%%% % Memory % %%%%%%%%%% \coordinate (memNorthWest) at ($ (sbl0.north west) -(2.0cm,0cm) $); \coordinate (memSouthEast) at ($ (sbl3.south west) -(0.5cm,0cm) $); \filldraw[style=nonvolatile,rounded corners] (memNorthWest) rectangle (memSouthEast) node [midway] {@{term "m\<^sub>s\<^sub>b\<^sub>h"}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Virtual memory boundaries % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% \draw [style=virtual,out=90,in=-90](sbr1.north east) to (sbr0.south west); \draw [style=virtual,out=90,in=-90](sbr2.north west) to (sbr1.south east); \draw [style=virtual,out=90,in=-90](sbr3.north west) to (sbr2.south west); \draw [style=virtual] ($ (memNorthWest) +(0,0.4cm) $) to ($ (sbr0.north west) + (-0.3,0.4)$)[out=0,in=90] to ($ (sbr0.north west)$); \draw [style=virtual]($ (memNorthWest |- memSouthEast) +(0,-0.8cm) $) to node[midway,above]{@{term m}} ($ (sbr3.south west) + (-0.3,-0.8)$)[out=0,in=90] to ($ (sbr3.south west)$); \node (execslabel)[below] at ($ (sbl3.south)$) {executed}; \node (suspendslabel)[below] at ($ (sbr3.south)$) {suspended}; \end{tikzpicture} \caption{Conflicting write in thread j (marked x) \label{fig:coupling-i-read-conflict}} \end{figure} We refer to x both for the write operation itself and to characterize the point in time in the computation of the virtual machine where the write was executed. At the point x the write was safe according to rules in Figure~\ref{fig:safe-delayed} for non-volatile writes. So it was owned by thread @{term j} and unshared. This knowledge about the safety of write x is preserved in the invariants, namely (Ownership.\ref{inv-ownership:owned-or-read-only}) and (Sharing.\ref{inv-sharing:non-volatile-writes-unshared}). Additionally from invariant (Sharing.\ref{inv-sharing:no-write-to-read-only-memory}) we know that address @{term a} was not read-only at point x. Now we combine this information with the safety of the read of thread @{term i} in the current configuration @{term "cfg"}: address @{term a} either has to be owned by thread @{term i}, or has to be read-only or the read is volatile and @{term a} is shared. Additionally there are the constraints on the released addresses which we will exploit below. Let us address all cases step by step. First, we consider that address @{term a} is currently owned by thread @{term i}. As it was owned by thread @{term j} at time x there has to be an release of @{term a} in the executed prefix of the store buffer of thread @{term j}. This release is recorded in the release set, so we know @{term "a \ dom (\s!j)"}. This contradicts the safety of the read. Second, we consider that address @{term a} is currently read-only. At time x address @{term a} was owned by thread @{term j}, unshared and not read-only. Hence there was a release of address @{term a} in the executed prefix of the store buffer of @{term j}, where it made a transition unshared and owned to shared. With the monotonicity of the release sets this means @{term "a \ dom (\s!j)"}, even more precisely @{term "(\s!j) a = Some False"}. Hence there is no chance to get the read safe (neiter a volatile nor a non-volatile). Third, consider a volatile read and that address @{term a} is currently shared. This is ruled out by the same line of reasoning as in the previous case. So ultimately we have ruled out all races that could destroy the value at address @{term a} and have shown that we can simulate the step on the virtual machine. This completes the simulation of the case where there is no store buffer forwarding and no volatile write in the store buffer of thread @{term i}. The other cases are handled similar. The main arguments are obtained by arguing about safety of configuration @{term cfg''} and exploiting the invariants to rule out conflicting operations in other store buffers. When there is a volatile write in he store buffer of thread @{term i} there are still pending suspended instructions in the virtual machine. Hence the virtual machine makes no step and we have to argue that the simulation relation as well as all invariants still hold. Up to now we have focused on how to simulate the read and in particular on how to argue that the value read in the store buffer machine is the same as the value read in the virtual machine. Besided these simulation properties another major part of the proof is to show that all invariants are maintained. For example if the non-volatile read enters the store buffer we have to argue that this new entry is either owned or refers to an read-only address (Ownership.\ref{inv-ownership:owned-or-read-only}). As for the simulation above this follows from safety of the virtual machine in configuration @{term "cfg''"}. However, consider an ghost operation that acquires an address @{term a}. From safety of the configuration @{term "cfg''"} we can only infer that there is no conflicting acquire in the non-volaitle prefixes of the other store buffers. In case an conflicting acquire is in the suspended part of a store buffer of thread @{term j} safety of configuration @{term "cfg''"} is not enough. But as we have safety of all reachable states we can forward the computation of thread @{term j} until the conflicting acquire is about to be executed and construct an unsafe state which rules out the conflict. Last we want to comment on the case where the store buffer takes a step. The major case destinction is wheter a volatile write leaves the store buffer or not. In the former case the virtual machine has to simulate a whole bunch of instructions at once to simulate the store buffer machine up to the next volatile write in the store buffer. In the latter case the virtual machine does no step at all, since the instruction leaving the store buffer is already simulated. In both cases one key argument is commutativity of non-volatile operations with respect to global effects on the memory or the sharing map. Consider a non-volatile store buffer step of thread @{term i}. In the configuration of the virtual machine before the store buffer step of thread @{term i}, the simulation relation applies the update to the memory and the sharing map of the store buffer machine, within the operations @{term "flush_all_until_volatile_write"} and @{term "share_all_until_volatile_write"} `somewhere in the middle' to obtain the memory and the sharing map of the virtual machine. After the store buffer step however, when the non-volatile operations has left the store buffer, the effect is applied to the memory and the sharing map right in the beginning. The invariants and safety sideconditions for non-volatile operations guarantee `locality' of the operation which manifests in commutativity properties. For example, a non-volatile write is thread local. There is no conflicting write in any other store buffer and hence the write can be safely moved to the beginning. This conludes the discussion on the proof of Theorem~\ref{thm:simulation}.\qed \ text (in xvalid_program_progress) \ \bigskip The simulation theorem for a single step is inductive and can therefor be extended to arbitrary long computations. Moreover, the coupling relation as well as the invariants become trivial for a initial configuration where all store buffers are empty and the ghost state is setup appropriately. To arrive at our final Theorem \ref{thm:reduction} we need the following steps: \begin{enumerate} \item \label{sim:sb-sbh} simulate the computation of the store buffer machine @{term "(ts\<^sub>s\<^sub>b,m,()) \\<^sub>s\<^sub>b\<^sup>* (ts\<^sub>s\<^sub>b',m',())"} by a computation of a store buffer machine with history @{term "(ts\<^sub>s\<^sub>b\<^sub>h,m,\) \\<^sub>s\<^sub>b\<^sub>h\<^sup>* (ts\<^sub>s\<^sub>b\<^sub>h',m',\')"}, \item \label{sim:sbh-delayed} simulate the computation of the store buffer machine with history by a computation of the virtual machine with delayed releases @{term "(ts,m,\) \\<^sub>d\<^sup>* (ts',m',\')"} by Theorem \ref{thm:simulation} (extended to the reflexive transitive closure), \item \label{sim:delayed-free-flowing} simulate the computation of the virtual machine with delayed releases by a computation of the virtual machine with free flowing releases @{term "(ts,m,\) \\<^sub>v\<^sup>* (ts',m',\')"}\footnote{Here we are sloppy with @{term ts}; strictly we would have to distinguish the thread configurations without the @{term \} component form the ones with the @{term \} component used for delayed releases}. \end{enumerate} Step \ref{sim:sb-sbh} is trivial since the bookkeeping within the additional ghost and history state does not affect the control flow of the transition systems and can be easily removed. Similar the additional @{term \} ghost component can be ignored in Step \ref{sim:delayed-free-flowing}. However, to apply Theorem \ref{thm:simulation} in Step \ref{sim:sbh-delayed} we have to convert from @{term [names_short] "safe_reach_virtual_free_flowing (ts, m, \)"} provided by the preconditions of Theorem \ref{thm:reduction} to the required @{term [names_short] "safe_reach_direct_delayed (ts, m, \)"}. This argument is more involved and we only give a short sketch here. -The other direction is trival as every single case for delayed releases (cf. Figure \ref{fig:safe-delayed}) immediately implies the corresponding case for free flowing releases (cf. Figure \ref{fig:safe-virtual-memory}). +The other direction is trivial as every single case for delayed releases (cf. Figure \ref{fig:safe-delayed}) immediately implies the corresponding case for free flowing releases (cf. Figure \ref{fig:safe-virtual-memory}). First keep in mind that the predicates ensure that \emph{all} reachable configurations starting from @{term "(ts,m,\)"} are safe, according to the rules for free flowing releases or delayed releases respectively. We show the theorem by contraposition and start with a computation which reaches a configuration @{term c} that is unsafe according to the rules for delayed releases and want to show that there has to be a (potentially other) computation (starting from the same initial state) that leads to an unsafe configuration @{term c'} accroding to free flowing releases. If @{term c} is already unsafe according to free flowing releases we have @{term "c'=c"} and are finished. Otherwise we have to find another unsafe configuration. Via induction on the length of the global computation we can also assume that for all shorter computations both safety notions coincide. A configuration can only be unsafe with respect to delayed releases and safe with respect to free flowing releases if there is a race between two distinct Threads @{term i} and @{term j} on an address @{term a} that is in the release set @{term "\"} of one of the threads, lets say Thread @{term i}. For example Thread @{term j} attempts to write to an address @{term a} which is in the release set of Thread @{term i}. If the release map would be empty there cannot be such an race (it would simulataneously be unsafe with respect to free flowing releases). Now we aim to find a configuration @{term c'} that is also reachable from the initial configuration and is unsafe with respect to free flowing releases. Intuitively this is a configuration where Thread @{term i} is rewinded to the state just before the release of address @{term a} and Thread @{term j} is in the same state as in configuration @{term c}. Before the release of @{term a} the address has to be owned by Thread @{term i}, which is unsafe according to free flowing releases as well as delayed releases. So we can argue that either Thread @{term j} can reach the same state although Thread @{term i} is rewinded or we even hit an unsafe configuration before. What kind of steps can Thread @{term i} perform between between the free flowing release point (point of the ghost instruction) and the delayed release point (point of next volatile write, interlocked operation or fence at which the release map is emptied)? How can these actions affect Thread @{term j}? Note that the delayed release point is not yet reached as this would empty the release map (which we know not to be empty). Thus Thread @{term i} does only perform reads, ghost instructions, program steps or non-volatile writes. All of these instructions of Thread @{term i} either have no influence on the computation of Thread @{term j} at all (e.g. a read, program step, non-volatile write or irrelevant ghost operation) or may cause a safety violation already in a shorter computation (e.g. acquiring an address that another thread holds). This is fine for our inductive argument. So either we can replay every step of Thread @{term j} and reach the final configuration @{term c'} which is now also unsafe according to free flowing releases, or we hit a configuration @{term c''} in a shorter computation which violates the rules of delayed as well as free flowing releases (using the induction hypothesis). \ section \PIMP \label{sec:pimp}\ text \ PIMP is a parallel version of IMP\cite{Nipkow-FSTTCS-96}, a canonical WHILE-language. An expression @{term "e"} is either \begin{inparaenum} \item @{term "Const v"}, a constant value, \item @{term "Mem volatile a"}, a (volatile) memory lookup at address @{term "a"}, \item @{term "Tmp sop"}, reading from the temporaries with a operation @{term "sop"} which is an intermediate expression occurring in the transition rules for statements, \item @{term "Unop f e"}, a unary operation where @{term "f"} is a unary function on values, and finally \item @{term "Binop f e\<^sub>1 e\<^sub>2"}, a binary operation where @{term "f"} is a binary function on values. \end{inparaenum} A statement @{term "s"} is either \begin{inparaenum} \item @{term "Skip"}, the empty statement, \item @{term "Assign volatile a e A L R W"}, a (volatile) assignment of expression @{term "e"} to address expression @{term "a"}, \item @{term "CAS a c\<^sub>e s\<^sub>e A L R W"}, atomic compare and swap at address expression @{term "a"} with compare expression @{term "c\<^sub>e"} and swap expression @{term "s\<^sub>e"}, \item @{term "Seq s\<^sub>1 s\<^sub>2"}, sequential composition, \item @{term "Cond e s\<^sub>1 s\<^sub>2"}, the if-then-else statement, \item @{term "While e s"}, the loop statement with condition @{term "e"}, \item @{term "SGhost"}, and @{term "SFence"} as stubs for the corresponding memory instructions. \end{inparaenum} The key idea of the semantics is the following: expressions are evaluated by issuing instructions to the memory system, then the program waits until the memory system has made all necessary results available in the temporaries, which allows the program to make another step. Figure~\ref{fig:expression-evaluation} defines expression evaluation. % \begin{figure} \begin{tabularx}{\textwidth}{l@ {~~\=\~~}X} @{thm (lhs) issue_expr.simps (1) [no_vars]} & @{thm (rhs) issue_expr.simps (1) [no_vars]}\\ @{thm (lhs) issue_expr.simps (2) [no_vars]} & @{thm (rhs) issue_expr.simps (2) [no_vars]}\\ @{thm (lhs) issue_expr.simps (3) [where sop="(D,f)", no_vars]} & @{thm (rhs) issue_expr.simps (3) [where sop="(D,f)", no_vars]}\\ @{thm (lhs) issue_expr.simps (4) [no_vars]} & @{thm (rhs) issue_expr.simps (4) [no_vars]}\\ @{thm (lhs) issue_expr.simps (5) [no_vars]} & @{thm [break,margin=50] (rhs) issue_expr.simps (5) [no_vars]}\\ \end{tabularx}\\[2pt] \begin{tabularx}{\textwidth}{l@ {~~\=\~~}X} @{thm (lhs) eval_expr.simps (1) [no_vars]} & @{thm (rhs) eval_expr.simps (1) [no_vars]}\\ @{thm (lhs) eval_expr.simps (2) [no_vars]} & @{thm (rhs) eval_expr.simps (2) [no_vars]}\\ @{thm (lhs) eval_expr.simps (3) [where sop="(D,f)", no_vars]} & @{thm (rhs) eval_expr.simps (3) [where sop="(D,f)",simplified fst_conv snd_conv, no_vars]}\\ @{thm (lhs) eval_expr.simps (4) [no_vars]} & @{thm (rhs) eval_expr.simps (4) [no_vars]}\\ @{thm (lhs) eval_expr.simps (5) [no_vars]} & @{thm [break,margin=50] (rhs) eval_expr.simps (5) [no_vars]} \end{tabularx} \caption{Expression evaluation\label{fig:expression-evaluation}} \end{figure} % The function @{term "used_tmps e"} calculates the number of temporaries that are necessary to evaluate expression @{term "e"}, where every @{term "Mem"} expression accounts to one temporary. With @{term "issue_expr t e"} we obtain the instruction list for expression @{term "e"} starting at temporary @{term "t"}, whereas @{term "eval_expr t e"} constructs the operation as a pair of the domain and a function on the temporaries. The program transitions are defined in Figure~\ref{fig:program-transitions}. We instantiate the program state by a tuple @{term "(s,t)"} containing the statement @{term "s"} and the temporary counter @{term "t"}. % \begin{figure} \begin{center} @{thm [mode=Rule] AssignAddr' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] Assign' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] CASAddr' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] CASComp' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] CAS' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] stmt_step.Seq [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] stmt_step.SeqSkip [no_vars]} \\[0.5\baselineskip] @{thm [mode=Rule] SCond' [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] stmt_step.CondTrue [no_vars]}\\[0.5\baselineskip] @{thm [mode=Rule] stmt_step.CondFalse [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] While [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] SGhost [no_vars]}\\[-0.3\baselineskip] @{thm [mode=Axiom] SFence [no_vars]}\\[0.1\baselineskip] \end{center} \caption{Program transitions\label{fig:program-transitions}} \end{figure} % To assign an expression @{term "e"} to an address(-expression) @{term "a"} we first create the memory instructions for evaluation the address @{term "a"} and transforming the expression to an operation on temporaries. The temporary counter is incremented accordingly. When the value is available in the temporaries we continue by creating the memory instructions for evaluation of expression @{term "e"} followed by the corresponding store operation. Note that the ownership annotations can depend on the temporaries and thus can take the calculated address into account. Execution of compare and swap @{term "CAS"} involves evaluation of three expressions, the address @{term "a"} the compare value @{term "c\<^sub>e"} and the swap value @{term "s\<^sub>e"}. It is finally mapped to the read-modify-write instruction @{term "RMW"} of the memory system. Recall that execution of @{term "RMW"} first stores the memory content at address @{term "a"} to the specified temporary. The condition compares this value with the result of evaluating @{term "c\<^sub>e"} and writes swap value @{term "s\<^sub>a"} if successful. In either case the temporary finally returns the old value read. Sequential composition is straightforward. An if-then-else is computed by first issuing the memory instructions for evaluation of condition @{term "e"} and transforming the condition to an operation on temporaries. When the result is available the transition to the first or second statement is made, depending on the result of @{const "isTrue"}. Execution of the loop is defined by stepwise unfolding. Ghost and fence statements are just propagated to the memory system. % To instantiate Theorem~\ref{thm:simulation} with PIMP we define the invariant parameter @{term "valid"}, which has to be maintained by all transitions of PIMP, the memory system and the store buffer. Let @{term "\"} be the valuation of temporaries in the current configuration, for every thread configuration @{term "ts\<^sub>s\<^sub>b!i = ((s,t),is,\,sb,\,\)"} where @{term "i < length ts\<^sub>s\<^sub>b"} we require: % \begin{inparaenum} \item The domain of all intermediate @{term "Tmp (D,f)"} expressions in statement @{term "s"} is below counter @{term "t"}. \item All temporaries in the memory system including the store buffer are below counter @{term "t"}. \item All temporaries less than counter @{term "t"} are either already defined in the temporaries @{term "\"} or are outstanding read temporaries in the memory system. \end{inparaenum} For the PIMP transitions we prove these invariants by rule induction on the semantics. For the memory system (including the store buffer steps) the invariants are straightforward. The memory system does not alter the program state and does not create new temporaries, only the PIMP transitions create new ones in strictly ascending order. \ (*<*) end (*>*)