diff --git a/thys/Gale_Shapley/Gale_Shapley1.thy b/thys/Gale_Shapley/Gale_Shapley1.thy --- a/thys/Gale_Shapley/Gale_Shapley1.thy +++ b/thys/Gale_Shapley/Gale_Shapley1.thy @@ -1,1614 +1,1823 @@ (* Stepwise refinement of the Gale-Shapley algorithm down to executable functional code. Author: Tobias Nipkow *) section "Part 1: Refinement down to lists" theory Gale_Shapley1 imports Main "HOL-Hoare.Hoare_Logic" "List-Index.List_Index" "HOL-Library.While_Combinator" "HOL-Library.LaTeXsugar" begin subsection \Misc\ lemmas conj12 = conjunct1 conjunct2 syntax "_assign_list" :: "idt \ nat \ 'b \ 'com" ("(2_[_] :=/ _)" [70, 0, 65] 61) translations "xs[n] := e" \ "xs := CONST list_update xs n e" abbreviation upt_set :: "nat \ nat set" ("{<_}") where "{ {0.. 'a \ 'a \ bool" where "prefers P x y = (index P x < index P y)" abbreviation prefa :: "'a list \ 'a \ 'a \ bool" ("(_ \/ _ < _)" [50,50,50] 50) where "P \ x < y \ prefers P x y" lemma prefers_asym: "P \ x < y \ \ P \ y < x" by(simp add: prefers_def) lemma prefers_trans: "P \ x < y \ P \ y < z \ P \ x < z" by (meson order_less_trans prefers_def) fun rk_of_pref :: "nat \ nat list \ nat list \ nat list" where "rk_of_pref r rs (n#ns) = (rk_of_pref (r+1) rs ns)[n := r]" | "rk_of_pref r rs [] = rs" definition ranking :: "nat list \ nat list" where "ranking P = rk_of_pref 0 (replicate (length P) 0) P" lemma length_rk_of_pref[simp]: "length(rk_of_pref v vs P) = length vs" by(induction P arbitrary: v)(auto) lemma nth_rk_of_pref: "\ length P \ length rs; i \ set P; distinct P; set P \ { \ rk_of_pref r rs P ! i = index P i + r" by(induction P arbitrary: r i) (auto simp add: nth_list_update) lemma ranking_iff_pref: "\ set P = { \ ranking P ! i < ranking P ! j \ P \ i < j" by(simp add: ranking_def prefers_def nth_rk_of_pref card_distinct) subsection \Fixing the preference lists\ type_synonym prefs = "nat list list" locale Pref = fixes n fixes P\<^sub>a :: prefs fixes P\<^sub>b :: prefs defines "n \ length P\<^sub>a" assumes length_P\<^sub>b: "length P\<^sub>b = n" assumes P\<^sub>a_set: "a < n \ length(P\<^sub>a!a) = n \ set(P\<^sub>a!a) = {b_set: "b < n \ length(P\<^sub>b!b) = n \ set(P\<^sub>b!b) = { bool" where "wf xs \ length xs = n \ set xs \ { wf A; a < n \ \ A!a < n" by (simp add: subset_eq) corollary wf_le_n1: "\ wf A; a < n \ \ A!a \ n-1" using wf_less_n by fastforce lemma sumA_ub: "wf A \ (\a n*(n-1)" using sum_bounded_above[of "{..The (termination) variant(s)\ text \Basic idea: either some \A!a\ is incremented or size of \M\ is incremented, but this cannot go on forever because in the worst case all \A!a = n-1\ and \M = n\. Because \n*(n-1) + n = n^2\, this leads to the following simple variant:\ definition var0 :: "nat list \ nat set \ nat" where [simp]: "var0 A M = (n^2 - ((\a { a \ M" shows "var0 A (M \ {a}) < var0 A M" proof - have 2: "M \ { { {a n*n" using sumA_ub[OF assms(1)] 0 by (simp add: algebra_simps le_diff_conv2) have "var0 (A[a' := A ! a' + 1]) M = n*n - (1 + (A ! a' + sum ((!) A) ({ = n^2 - (1 + (\a < n^2 - ((\a nat set \ nat" where [simp]: "var A M = (n^2 - n + 1 - ((\a n-1" "\a < n. a \ a' \ A!a \ n-2" shows "(\a (n-1)*(n-1)" proof - have "(\aa \ ({ {a'}. A!a)" by (simp add: assms(1) atLeast0LessThan insert_absorb) also have "\ =(\a \ { \ (\a \ { \ (n-1)*(n-2) + (n-1)" using sum_bounded_above[of "{.. = (n-1)*(n-1)" by (metis Suc_diff_Suc Suc_eq_plus1 add.commute diff_is_0_eq' linorder_not_le mult_Suc_right mult_cancel_left nat_1_add_1) finally show ?thesis . qed definition "match A a = P\<^sub>a ! a ! (A ! a)" lemma match_less_n: "\ wf A; a < n \ \ match A a < n" by (metis P\<^sub>a_set atLeastLessThan_iff match_def nth_mem subset_eq) lemma match_upd_neq: "\ wf A; a < n; a \ a' \ \ match (A[a := b]) a' = match A a'" by (simp add: match_def) definition blocks :: "nat list \ nat \ nat \ bool" where "blocks A a a' = (P\<^sub>a ! a \ match A a' < match A a \ P\<^sub>b ! match A a' \ a < a')" definition stable :: "nat list \ nat set \ bool" where "stable A M = (\(\a\M. \a'\M. a \ a' \ blocks A a a'))" text \The set of Bs that an A would prefer to its current match, i.e. all Bs above its current match \A!a\.\ abbreviation preferred where "preferred A a == nth (P\<^sub>a!a) ` { inj_on (match A) M)" text \If \a'\ is unmatched and final then all other \a\ are matched:\ lemma final_last: assumes M: "M \ { match A ` M" and a: "a < n \ a \ M" and final: "A ! a + 1 = n" shows "insert a M = {a ! a) ` {a_set a map_nth set_map set_upt) hence "inj_on ((!) (P\<^sub>a ! a)) {a ! a)) {a_set a final by (simp add: card_image) have 2: "card ?B \ card M" by(rule surj_card_le[OF subset_eq_atLeast0_lessThan_finite[OF M] pref_match']) have 3: "card M < n" using M a by (metis atLeast0LessThan card_seteq order.refl finite_atLeastLessThan le_neq_implies_less lessThan_iff subset_eq_atLeast0_lessThan_card) have "Suc (card M) = n" using 1 2 3 by simp thus ?thesis using M a by (simp add: card_subset_eq finite_subset) qed lemma more_choices: assumes A: "matching A M" and M: "M \ { { match A ` M" and "a < n" and matched: "match A a \ match A ` M" shows "A ! a + 1 < n" proof (rule ccontr) have match: "match A ` M \ {a_set unfolding matching_def by (smt (verit, best) atLeastLessThan_iff match_def image_subsetI in_mono nth_mem) have "card M < n" using M by (metis card_atLeastLessThan card_seteq diff_zero finite_atLeastLessThan not_less) assume "\ A ! a + 1 < n" hence "A ! a + 1 = n" using A \a < n\ unfolding matching_def by (metis add.commute wf_less_n linorder_neqE_nat not_less_eq plus_1_eq_Suc) hence *: "nth (P\<^sub>a ! a) ` { match A ` M" using pref_match' matched less_Suc_eq match_def by fastforce have "nth (P\<^sub>a!a) ` {a < n\ map_nth P\<^sub>a_set set_map set_upt) hence "match A ` M = {card M < n\ unfolding matching_def by (metis atLeast0LessThan card_image card_lessThan nat_neq_iff) qed corollary more_choices_matched: assumes "matching A M" "M \ { { match A ` M" and "a \ M" shows "A ! a + 1 < n" using more_choices[OF assms(1-4)] \a \ M\ \M \ { atLeastLessThan_iff by blast lemma atmost1_final: assumes M: "M \ {a match A ` M" shows "\\<^sub>\\<^sub>1 a. a < n \ a \ M \ A ! a + 1 = n" apply rule subgoal for x y using final_last[OF M inj, of x] final_last[OF M inj, of y] assms(3) by blast done lemma sumA_UB: assumes "matching A M" "M \ { {a match A ` M" shows "(\a (n-1)^2" proof - have M: "\a\M. A!a + 1 < n" using more_choices_matched[OF assms(1-3)] assms(4) \M \ { atLeastLessThan_iff by blast note Ainj = conj12[OF assms(1)[unfolded matching_def]] show ?thesis proof (cases "\a' M \ A!a' + 1 = n") case True then obtain a' where a': "a' M" "A!a' + 1 = n" using \M \ { \M \ { by blast hence "\a a' \ A!a \ n-2" using Uniq_D[OF atmost1_final[OF assms(2) Ainj(2) assms(4)], of a'] M wf_le_n1[OF Ainj(1)] by (metis Suc_1 Suc_eq_plus1 add_diff_cancel_right' add_le_imp_le_diff diff_diff_left less_eq_Suc_le order_less_le) from sumA_ub2[OF a'(1) _ this] a'(3) show ?thesis unfolding power2_eq_square by linarith next case False hence "\a' M \ A ! a' + 1 < n" by (metis Suc_eq_plus1 Suc_lessI wf_less_n[OF Ainj(1)]) with M have "\aa n*(n-2)" using sum_bounded_above[of "{.. \ (n-1)*(n-1)" by(simp add: algebra_simps) finally show ?thesis unfolding power2_eq_square . qed qed lemma var_ub: assumes "matching A M" "M \ { {a match A ` M" shows "(\a { n^2 + 1 - 2*n" using sumA_UB[OF assms(1-4)] by (simp add: power2_eq_square algebra_simps) have 4: "2*n \ Suc (n^2)" using le_square[of n] unfolding power2_eq_square by (metis Suc_1 add_mono_thms_linordered_semiring(1) le_SucI mult_2 mult_le_mono1 not_less_eq_eq plus_1_eq_Suc) show "(\a { {a match A ` M" "a \ M" shows "var A (M \ {a}) < var A M" proof - have 2: "M \ { n^2 + 1 - 2*n" using sumA_UB[OF assms(1-4)] by (simp add: power2_eq_square algebra_simps) have 5: "2*n \ Suc (n^2)" using le_square[of n] unfolding power2_eq_square by (metis Suc_1 add_mono_thms_linordered_semiring(1) le_SucI mult_2 mult_le_mono1 not_less_eq_eq plus_1_eq_Suc) have 6: "(\aa \ M\ finite_subset[OF assms(2)] by(simp) qed lemma var_next: assumes"matching A M" "M \ { {a match A ` M" "a < n" shows "var (A[a := A ! a + 1]) M < var A M" proof - have "var (A[a := A ! a + 1]) M = n*n - n + 1 - (1 + (A ! a + sum ((!) A) ({ = n^2 - n + 1 - (1 + (\a < n^2 - n + 1 - ((\aAuxiliary Predicates\ text \The following two predicates express the same property: if \a\ prefers \b\ over \a\'s current match, then \b\ is matched with an \a'\ that \b\ prefers to \a\.\ definition pref_match where "pref_match A M = (\aba!a \ b < match A a \ (\a'\M. b = match A a' \ P\<^sub>b ! b \ a' < a))" definition pref_match' where "pref_match' A M = (\ab \ preferred A a. \a'\M. b = match A a' \ P\<^sub>b ! b \ a' < a)" lemma pref_match'_iff: "wf A \ pref_match' A M = pref_match A M" apply (auto simp add: pref_match'_def pref_match_def imp_ex prefers_def match_def) apply (smt (verit) P\<^sub>a_set atLeast0LessThan order.strict_trans index_first lessThan_iff linorder_neqE_nat nth_index) by (smt (verit, best) P\<^sub>a_set atLeast0LessThan card_atLeastLessThan card_distinct diff_zero in_mono index_nth_id lessThan_iff less_trans nth_mem) definition opti\<^sub>a where "opti\<^sub>a A = (\A'. matching A' { stable A' { (\aa ! a \ match A' a < match A a))" definition pessi\<^sub>b where "pessi\<^sub>b A = (\A'. matching A' { stable A' { (\aa' P\<^sub>b ! match A a \ a < a'))" lemma opti\<^sub>a_pessi\<^sub>b: assumes "opti\<^sub>a A" shows "pessi\<^sub>b A" unfolding pessi\<^sub>b_def proof (safe, goal_cases) case (1 A' a a') have "\ P\<^sub>a!a \ match A a < match A' a" using 1 by (metis atLeast0LessThan blocks_def lessThan_iff prefers_asym stable_def) with 1 \opti\<^sub>a A\ show ?case using P\<^sub>a_set match_less_n opti\<^sub>a_def prefers_def unfolding matching_def by (metis (no_types) atLeast0LessThan inj_on_contraD lessThan_iff less_not_refl linorder_neqE_nat nth_index) qed lemma opti\<^sub>a_inv: assumes A: "wf A" and a: "a < n" and a': "a' < n" and same_match: "match A a' = match A a" and pref: "P\<^sub>b ! match A a' \ a' < a" and "opti\<^sub>a A" shows "opti\<^sub>a (A[a := A ! a + 1])" proof (unfold opti\<^sub>a_def matching_def, rule notI, elim exE conjE) note opti\<^sub>a = \opti\<^sub>a A\[unfolded opti\<^sub>a_def matching_def] let ?A = "A[a := A ! a + 1]" fix A' a'' assume "a'' < n" and A': "length A' = n" "set A' \ {a ! a'' \ match A' a'' < match ?A a''" show False proof cases assume [simp]: "a'' = a" have "A!a < n" using A a by(simp add: subset_eq) with A A' a pref_a'' have "P\<^sub>a ! a \ match A' a < match A a \ match A' a = match A a" apply(auto simp: prefers_def match_def) by (smt (verit) P\<^sub>a_set wf_less_n card_atLeastLessThan card_distinct diff_zero index_nth_id not_less_eq not_less_less_Suc_eq) thus False proof assume "P\<^sub>a ! a \ match A' a < match A a " thus False using opti\<^sub>a A' \a < n\ by(fastforce) next assume "match A' a = match A a" have "a \ a'" using pref a' by(auto simp: prefers_def) hence "blocks A' a' a" using opti\<^sub>a pref A' same_match \match A' a = match A a\ a a' unfolding blocks_def by (metis P\<^sub>a_set atLeast0LessThan match_less_n inj_onD lessThan_iff linorder_neqE_nat nth_index prefers_def) thus False using a a' \a \ a'\ A'(3) by (metis stable_def atLeastLessThan_iff zero_le) qed next assume "a'' \ a" thus False using opti\<^sub>a A' pref_a'' \a'' < n\ by(metis match_def nth_list_update_neq) qed qed lemma pref_match_stable: "\ matching A { \ stable A {Algorithm 1\ definition invAM where [simp]: "invAM A M = (matching A M \ M \ { pref_match A M \ opti\<^sub>a A)" lemma invAM_match: "\ invAM A M; a < n \ a \ M; match A a \ match A ` M \ \ invAM A (M \ {a})" by(simp add: pref_match_def) lemma invAM_swap: assumes "invAM A M" assumes a: "a < n \ a \ M" and a': "a' \ M \ match A a' = match A a" and pref: "P\<^sub>b ! match A a' \ a < a'" shows "invAM (A[a' := A!a'+1]) (M - {a'} \ {a})" proof - have A: "wf A" and M : "M \ {a A" by(insert \invAM A M\) (auto) have "M \ { a'" using a' a M by auto have pref_match': "pref_match' A M" using pref_match pref_match'_iff[OF A] by blast let ?A = "A[a' := A!a'+1]" let ?M = "M - {a'} \ {a}" have neq_a': "\x. x \ ?M \ a' \ x" using \a \ a'\ by blast have \set ?A \ { apply(rule set_update_subsetI[OF A[THEN conjunct2]]) using more_choices[OF _ M \M \ {] A inj pref_match' a' subsetD[OF M, of a'] by(fastforce simp: pref_match'_def) hence "wf ?A" using A by(simp) moreover have "inj_on (match ?A) ?M" using a a' inj by(simp add: match_def inj_on_def)(metis Diff_iff insert_iff nth_list_update_neq) moreover have "pref_match' ?A ?M" using a a' pref_match' A pref \a' < n\ apply(simp add: pref_match'_def match_upd_neq neq_a' Ball_def Bex_def image_iff imp_ex nth_list_update less_Suc_eq flip: match_def) by (metis prefers_trans) moreover have "opti\<^sub>a ?A" using opti\<^sub>a_inv[OF A \a' < n\ _ _ _ \opti\<^sub>a A\] a a'[THEN conjunct2] pref by auto ultimately show ?thesis using a a' M pref_match'_iff by auto qed lemma invAM_next: assumes "invAM A M" assumes a: "a < n \ a \ M" and a': "a' \ M \ match A a' = match A a" and pref: "\ P\<^sub>b ! match A a' \ a < a'" shows "invAM (A[a := A!a + 1]) M" proof - have A: "wf A" and M : "M \ {a: "opti\<^sub>a A" and "a' < n" by(insert \invAM A M\ a') (auto) hence pref': "P\<^sub>b ! match A a' \ a' < a" using pref a a' P\<^sub>b_set unfolding prefers_def by (metis match_def match_less_n index_eq_index_conv linorder_less_linear subsetD) have "M \ {x. x\ M \ a \ x" using a by blast have pref_match': "pref_match' A M" using pref_match pref_match'_iff[OF A,of M] by blast hence "\a match A ` M" unfolding pref_match'_def by blast hence "A!a + 1 < n" using more_choices[OF _ M \M \ {] A inj a a' unfolding matching_def by (metis (no_types, lifting) imageI) let ?A = "A[a := A!a+1]" have "wf ?A" using A \A!a + 1 < n\ by(simp add: set_update_subsetI) moreover have "inj_on (match ?A) M" using a inj by(simp add: match_def inj_on_def) (metis nth_list_update_neq) moreover have "pref_match' ?A M" using a pref_match' pref' A a' neq_a by(auto simp: match_upd_neq pref_match'_def Ball_def Bex_def image_iff nth_list_update imp_ex less_Suc_eq simp flip: match_def) moreover have "opti\<^sub>a ?A" using opti\<^sub>a_inv[OF A conjunct1[OF a] \a' < n\ conjunct2[OF a'] pref' opti\<^sub>a] . ultimately show ?thesis using M by (simp add: pref_match'_iff) qed lemma Gale_Shapley1: "VARS M A a a' [M = {} \ A = replicate n 0] WHILE M \ { a \ M); IF match A a \ match A ` M THEN M := M \ {a} ELSE a' := (SOME a'. a' \ M \ match A a' = match A a); IF P\<^sub>b ! match A a' \ a < a' THEN A[a'] := A!a'+1; M := M - {a'} \ {a} ELSE A[a] := A!a+1 FI FI OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case by(auto simp: stable_def opti\<^sub>a_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def) next case 3 thus ?case using pref_match_stable by auto next case (2 v M A a) hence invAM: "invAM A M" and m: "matching A M" and M: "M \ { {a A" and v: "var A M = v" by auto note Ainj = conj12[OF m[unfolded matching_def]] note pref_match' = pref_match[THEN pref_match'_iff[OF Ainj(1), THEN iffD2]] hence pref_match1: "\a match A ` M" unfolding pref_match'_def by blast define a where "a = (SOME a. a < n \ a \ M)" have a: "a < n \ a \ M" unfolding a_def using M by (metis (no_types, lifting) atLeastLessThan_iff someI_ex subsetI subset_antisym) show ?case (is "?P((SOME a. a < n \ a \ M))") unfolding a_def[symmetric] proof - show "?P a" (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") proof (rule; rule) assume ?not_matched show ?THEN proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case using invAM_match[OF invAM a \?not_matched\] . case 2 show ?case using var_match[OF m M pref_match1] var0_match[OF Ainj(1) M(1)] a unfolding v by blast qed next assume matched: "\ ?not_matched" define a' where "a' = (SOME a'. a' \ M \ match A a' = match A a)" have a': "a' \ M \ match A a' = match A a" unfolding a'_def using matched by (smt (verit) image_iff someI_ex) hence "a' < n" "a \ a'" using a M atLeast0LessThan by auto show ?ELSE (is "?P((SOME a'. a' \ M \ match A a' = match A a))") unfolding a'_def[symmetric] proof - show "?P a'" (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") proof (rule; rule) assume ?pref show ?THEN proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case by(rule invAM_swap[OF invAM a a' \?pref\]) case 2 have "card(M - {a'} \ {a}) = card M" using a a' card.remove subset_eq_atLeast0_lessThan_finite[OF M(1)] by fastforce thus ?case using v var_next[OF m M pref_match1 \a' < n\] var0_next[OF Ainj(1) M \a' < n\] by simp qed next assume "\ ?pref" show ?ELSE proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case using invAM_next[OF invAM a a' \\ ?pref\] . case 2 show ?case using a v var_next[OF m M pref_match1, of a] var0_next[OF Ainj(1) M, of a] by simp qed qed qed qed qed qed text \Proof also works for @{const var0} instead of @{const var}.\ subsection \Algorithm 2: List of unmatched As\ abbreviation invas where "invas as == (set as \ { distinct as)" lemma Gale_Shapley2: "VARS A a a' as [as = [0.. A = replicate n 0] WHILE as \ [] INV { invAM A ({ invas as} VAR {var A ({ match A ` ({ { match A a' = match A a); IF P\<^sub>b ! match A a' \ a < a' THEN A[a'] := A!a'+1; as := a' # tl as ELSE A[a] := A!a+1 FI FI OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case by(auto simp: stable_def opti\<^sub>a_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def) next case 3 thus ?case using pref_match_stable by auto next case (2 v A _ a' as) let ?M = "{ {a A" and "as \ []" and v: "var A ?M = v" and as: "invas as" using 2 by auto note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast from \as \ []\ obtain a as' where aseq: "as = a # as'" by (fastforce simp: neq_Nil_conv) have set_as: "?M \ {a} = { a \ ?M" using as unfolding aseq by (simp) show ?case proof (simp only: aseq list.sel, goal_cases) case 1 show ?case (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") proof (rule; rule) assume ?not_matched then have nm: "match A a \ match A ` ?M" unfolding aseq . show ?THEN proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case using invAM_match[OF \invAM A ?M\ a nm] as unfolding set_as by (simp add: aseq) case 2 show ?case using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan unfolding set_as v by blast qed next assume matched: "\ ?not_matched" define a' where "a' = (SOME a'. a' \ ?M \ match A a' = match A a)" have a': "a' \ ?M \ match A a' = match A a" unfolding a'_def aseq using matched by (smt (verit) image_iff someI_ex) hence "a' < n" "a \ a'" using a M atLeast0LessThan by auto show ?ELSE unfolding aseq[symmetric] a'_def[symmetric] proof (goal_cases) case 1 show ?case (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") proof (rule; rule) assume ?pref show ?THEN proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) have *: "{ {a} = {invAM A ?M\ a a' \?pref\] unfolding * using a' as aseq by force case 2 have "card({a' < n\ a atLeast0LessThan by (metis Suc_eq_plus1 lessThan_iff var_def) qed next assume "\ ?pref" show ?ELSE proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case using invAM_next[OF \invAM A ?M\ a a' \\ ?pref\] as by blast case 2 show ?case using a v var_next[OF m M _ pref_match1, of a] by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) qed qed qed qed qed qed subsection \Algorithm 3: Record matching of Bs to As\ abbreviation invAB :: "nat list \ (nat \ nat option) \ nat set \ bool" where "invAB A B M == (ran B = M \ (\b a. B b = Some a \ match A a = b))" lemma invAB_swap: assumes invAB: "invAB A B M" assumes a: "a < n \ a \ M" and a': "a' \ M \ match A a' = match A a" and "inj_on B (dom B)" "B(match A a) = Some a'" shows "invAB (A[a' := A!a'+1]) (B(match A a := Some a)) (M - {a'} \ {a})" proof - have "\b x. b \ match A a \ B b = Some x \ a'\ x" using invAB a' by blast moreover have "a \ a'" using a a' by auto ultimately show ?thesis using assms by(simp add: ran_map_upd_Some match_def) qed lemma Gale_Shapley3: "VARS A B a a' as b [as = [0.. A = replicate n 0 \ B = (\_. None)] WHILE as \ [] INV { invAM A ({ invAB A B ({ invas as} VAR {var A ({b ! match A a' \ a < a' THEN B := B(b := Some a); A[a'] := A!a'+1; as := a' # tl as ELSE A[a] := A!a+1 FI FI OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case by(auto simp: stable_def opti\<^sub>a_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def) next case 3 thus ?case using pref_match_stable by auto next case (2 v A B _ a' as) let ?M = "{ {a A" and "as \ []" and v: "var A ?M = v" and as: "invas as" and invAB: "invAB A B ?M" using 2 by auto note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast from \as \ []\ obtain a as' where aseq: "as = a # as'" by (fastforce simp: neq_Nil_conv) have set_as: "?M \ {a} = { a \ ?M" using as unfolding aseq by (simp) show ?case proof (simp only: aseq list.sel, goal_cases) case 1 show ?case (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") proof (rule; rule) assume ?not_matched then have nm: "match A a \ match A ` ?M" using invAB unfolding aseq ran_def apply (clarsimp simp: set_eq_iff) using not_None_eq by blast show ?THEN proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) have invAM': "invAM A ({?not_matched\ set_as by (simp) case 1 show ?case using invAM' as invAB' unfolding set_as aseq by (metis distinct.simps(2) insert_subset list.simps(15)) case 2 show ?case using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan unfolding set_as v by blast qed next assume matched: "\ ?not_matched" then obtain a' where a'eq: "B(match A a) = Some a'" by auto have a': "a' \ ?M \ match A a' = match A a" unfolding aseq using a'eq invAB by (metis ranI aseq) hence "a' < n" "a \ a'" using a M atLeast0LessThan by auto show ?ELSE unfolding aseq[symmetric] a'eq option.sel proof (goal_cases) have inj_dom: "inj_on B (dom B)" by (metis (mono_tags) domD inj_onI invAB) case 1 show ?case (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") proof (rule; rule) assume ?pref show ?THEN proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) have *: "{ {a} = {b x. b \ match A a \ B b = Some x \ a'\ x" using invAB a' by blast have invAB': "invAB (A[a' := A ! a' + 1]) (B(match A a := Some a)) ({?pref\] invAB' unfolding * using a' as aseq by simp case 2 have "card({a' < n\ a atLeast0LessThan by (metis Suc_eq_plus1 lessThan_iff var_def) qed next assume "\ ?pref" show ?ELSE proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 have "invAB (A[a := A ! a + 1]) B ?M" using invAB a by (metis match_def nth_list_update_neq ranI) thus ?case using invAM_next[OF invAM a a' \\ ?pref\] as by blast case 2 show ?case using a v var_next[OF m M _ pref_match1, of a] by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) qed qed qed qed qed qed subsection \Unused data refinement step\ (* begin unused: directly implement function B via lists B and M (data refinement); also done in Alg. 4 in a more principled manner *) abbreviation invAB' :: "nat list \ nat list \ bool list \ nat set \ bool" where "invAB' A B M M' == (length B = n \ length M = n \ M' = nth B ` {b. b < n \ M!b} \ (\b B!b < n \ match A (B!b) = b))" lemma Gale_Shapley4_unused: "VARS A B M a a' as b [as = [0.. A = replicate n 0 \ B = replicate n 0 \ M = replicate n False] WHILE as \ [] INV { invAM A ({ invAB' A B M ({ invas as} VAR {var A ({ (M ! b) THEN M[b] := True; B[b] := a; as := tl as ELSE a' := B ! b; IF P\<^sub>b ! match A a' \ a < a' THEN B[b] := a; A[a'] := A!a'+1; as := a' # tl as ELSE A[a] := A!a+1 FI FI OD [wf A \ inj_on (match A) { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case by(auto simp: stable_def opti\<^sub>a_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def) next case 3 thus ?case using pref_match_stable by auto next case (2 v A B M _ a' as) let ?M = "{ {a A" and notall: "as \ []" and v: "var A ?M = v" and as: "invas as" and invAB: "invAB' A B M ?M" using 2 by auto note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast from notall obtain a as' where aseq: "as = a # as'" by (fastforce simp: neq_Nil_conv) have set_as: "?M \ {a} = { a \ ?M" using as unfolding aseq by (simp) show ?case proof (simp only: aseq list.sel, goal_cases) case 1 show ?case (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") proof (rule; rule) assume ?not_matched then have nm: "match A a \ match A ` ?M" using invAB set_as unfolding aseq by force show ?THEN proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) have invAM': "invAM A ({?not_matched\ set_as match_less_n[OF A] a by (auto simp add: image_def nth_list_update) case 1 show ?case using invAM' invAB as invAB' unfolding set_as aseq by (metis distinct.simps(2) insert_subset list.simps(15)) case 2 show ?case using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan unfolding set_as v by blast qed next assume matched: "\ ?not_matched" hence "match A a \ match A ` ({ ?M \ match A ?a = match A a" using invAB match_less_n[OF A] matched a by blast hence "?a < n" "a \ ?a" using a by auto show ?ELSE unfolding aseq option.sel proof (goal_cases) case 1 show ?case (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") proof (rule; rule) assume ?pref show ?THEN proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) have *: "{ {a} = {b match A a \ M!b \ ?a \ B!b" using invAB a' by metis have invAB': "invAB' (A[?a := A ! ?a + 1]) (B[match A a := a]) M ({a \ ?a\ a' match_less_n[OF A, of a] a apply (simp add: nth_list_update) apply rule apply(auto simp add: image_def)[] apply (clarsimp simp add: match_def) apply (metis (opaque_lifting) nth_list_update_neq) done case 1 show ?case using invAM_swap[OF invAM a a' \?pref\] invAB' unfolding * using a' as aseq by (auto) case 2 have "card({?a < n\ a atLeast0LessThan by (metis Suc_eq_plus1 lessThan_iff var_def) qed next assume "\ ?pref" show ?ELSE proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 have "invAB' (A[a := A ! a + 1]) B M ({a \ ?a\ by (metis match_def nth_list_update_neq) thus ?case using invAM_next[OF invAM a a' \\ ?pref\] as aseq by fastforce case 2 show ?case using a v var_next[OF m M _ pref_match1, of a] aseq by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) qed qed qed qed qed qed subsection \Algorithm 4: remove list of unmatched As\ lemma Gale_Shapley4: "VARS A B ai a a' [ai = 0 \ A = replicate n 0 \ B = (\_. None)] WHILE ai < n INV { invAM A { invAB A B { ai \ n } VAR {z = n - ai} DO a := ai; WHILE B (match A a) \ None INV { invAM A ({ invAB A B ({ (a \ ai \ ai < n) \ z = n-ai } VAR {var A {b ! match A a' \ a < a' THEN B := B(match A a := Some a); A[a'] := A!a'+1; a := a' ELSE A[a] := A!a+1 FI OD; B := B(match A a := Some a); ai := ai+1 OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case (* outer invar holds initially *) by(auto simp: stable_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def)[] next case 2 (* outer invar and b ibplies inner invar *) thus ?case by (auto simp: atLeastLessThanSuc_atLeastAtMost simp flip: atLeastLessThan_eq_atLeastAtMost_diff) next case (4 z A B ai a) (* inner invar' and not b ibplies outer invar *) note inv = 4[THEN conjunct1] note invAM = inv[THEN conjunct1] note aai = inv[THEN conjunct2,THEN conjunct2] show ?case proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 have *: "{ match A a \ match A ` ({ match A ` ({ { None" and "a \ ai" and v: "var A ?M = v" and as: "a \ ai \ ai < n" and invAB: "invAB A B ?M" using 3 by auto note invar = 3[THEN conjunct1,THEN conjunct1] note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast from matched obtain a' where a'eq: "B(match A a) = Some a'" by auto have a': "a' \ ?M \ match A a' = match A a" using a'eq invAB by (metis ranI) have a: "a < n \ a \ ?M" using invar by auto have "?M \ {a \ ai\ by simp show ?case unfolding a'eq option.sel proof (goal_cases) case 1 show ?case (is "(?unstab \ ?THEN) \ (\ ?unstab \ ?ELSE)") proof (rule; rule) assume ?unstab have *: "{ {a} = { a)) ({?unstab\] invAB' invar a' unfolding * by (simp) next case 2 show ?case using v var_next[OF m M \?M \ { pref_match1 \a' < n\] card by (metis var_def Suc_eq_plus1 psubset_eq) qed next assume "\ ?unstab" show ?ELSE proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) have *: "\b a'. B b = Some a' \ a \ a'" by (metis invAB ranI a) case 1 show ?case using invAM_next[OF invAM a a' \\ ?unstab\] invar * by (simp add: match_def) next case 2 show ?case using v var_next[OF m M \?M \ { pref_match1, of a] a card by (metis Suc_eq_plus1 var_def) qed qed qed qed -subsection \Algorithm 4': single-loop variant\ +subsubsection \Algorithm 4.1: single-loop variant\ -lemma Gale_Shapley4': "VARS A B a a' ai b +lemma Gale_Shapley4_1: "VARS A B a a' ai b [ai = 0 \ a = 0 \ A = replicate n 0 \ B = (\_. None)] WHILE ai < n INV { invAM A ({ invAB A B ({ (a \ ai \ ai \ n) \ (ai=n \ a=ai)} VAR {var A ({b ! match A a' \ a < a' THEN B := B(b := Some a); A[a'] := A!a'+1; a := a' ELSE A[a] := A!a+1 FI FI OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case by(auto simp: stable_def opti\<^sub>a_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def) next case 3 thus ?case using pref_match_stable using atLeast0_lessThan_Suc by force next case (2 v A B a a' ai b) let ?M = "{ { ai" and v: "var A ?M = v" and as: "a \ ai \ ai < n" and invAB: "invAB A B ?M" using 2 by auto note invar = 2[THEN conjunct1,THEN conjunct1] note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast have a: "a < n \ a \ ?M" using as by (simp) show ?case (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") proof (rule; rule) assume ?not_matched then have nm: "match A a \ match A ` ?M" using invAB unfolding ran_def apply (clarsimp simp: set_eq_iff) using not_None_eq by blast show ?THEN proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) have *: "{ {a} = {a \ ai\ by auto hence invAM': "invAM A {?not_matched\ ** by (simp) case 1 show ?case using invAM' as invAB' * by presburger case 2 show ?case using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan * ** unfolding v by (metis lessThan_iff) qed next assume matched: "\ ?not_matched" then obtain a' where a'eq: "B(match A a) = Some a'" by auto have a': "a' \ ?M \ match A a' = match A a" using a'eq invAB by (metis ranI) hence "a' < n" "a \ a'" using a M atLeast0LessThan by auto show ?ELSE unfolding a'eq option.sel proof (goal_cases) have inj_dom: "inj_on B (dom B)" by (metis (mono_tags) domD inj_onI invAB) case 1 show ?case (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") proof (rule; rule) assume ?pref show ?THEN proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) have *: "{ {a} = {b x. b \ match A a \ B b = Some x \ a'\ x" using invAB a' by blast have invAB': "invAB (A[a' := A ! a' + 1]) (B(match A a := Some a)) ({?pref\] invAB' unfolding * using a' as by simp case 2 have "card({a' < n\ a atLeast0LessThan by (metis Suc_eq_plus1 lessThan_iff var_def) qed next assume "\ ?pref" show ?ELSE proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 have "invAB (A[a := A ! a + 1]) B ?M" using invAB a by (metis match_def nth_list_update_neq ranI) thus ?case using invAM_next[OF invAM a a' \\ ?pref\] using "2" by presburger case 2 show ?case using a v var_next[OF m M _ pref_match1, of a] by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) qed qed qed qed qed subsection \Algorithm 5: Data refinement of \B\\ definition "\ B M = (\b. if b < n \ M!b then Some(B!b) else None)" lemma \_Some[simp]: "\ B M b = Some a \ b < n \ M!b \ a = B!b" by(auto simp add: \_def) lemma \update1: "\ \ M ! b; b < length B; b < length M; n = length M \ \ ran(\ (B[b := a]) (M[b := True])) = ran(\ B M) \ {a}" by(force simp add: \_def ran_def nth_list_update) lemma \update2: "\ M!b; b < length B; b < length M; length M = n \ \ \ (B[b := a]) M = (\ B M)(b := Some a)" by(force simp add: \_def nth_list_update) abbreviation invAB2 :: "nat list \ nat list \ bool list \ nat set \ bool" where "invAB2 A B M M' == (invAB A (\ B M) M' \ (length B = n \ length M = n))" definition invar1 where [simp]: "invar1 A B M ai = (invAM A { invAB2 A B M { ai \ n)" definition invar2 where [simp]: "invar2 A B M ai a \ (invAM A ({ invAB2 A B M ({ a \ ai \ ai < n)" lemma Gale_Shapley5: "VARS A B M ai a a' [ai = 0 \ A = replicate n 0 \ length B = n \ M = replicate n False] WHILE ai < n INV { invar1 A B M ai } VAR { z = n - ai} DO a := ai; WHILE M ! match A a INV { invar2 A B M ai a \ z = n-ai } VAR {var A {b ! match A a' \ a < a' THEN B[match A a] := a; A[a'] := A!a'+1; a := a' ELSE A[a] := A!a+1 FI OD; B[match A a] := a; M[match A a] := True; ai := ai+1 OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case (* outer invar holds initially *) by(auto simp: pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) next case 2 (* outer invar and b ibplies inner invar *) thus ?case by (auto simp: atLeastLessThanSuc_atLeastAtMost simp flip: atLeastLessThan_eq_atLeastAtMost_diff) next case (4 z A B M ai a) (* inner invar' and not b ibplies outer invar *) note inv = 4[THEN conjunct1, unfolded invar2_def] note invAM = inv[THEN conjunct1,THEN conjunct1] note aai = inv[THEN conjunct1, THEN conjunct2, THEN conjunct2] show ?case proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 have *: "{ match A a \ match A ` ({ match A ` ({update1 match_less_n insert_absorb nth_list_update) next case 2 thus ?case using 4 by auto qed next case 5 (* outer invar and not b ibplies post *) thus ?case using pref_match_stable unfolding invAM_def invar1_def by(metis le_neq_implies_less) next case (3 z v A B M ai a) (* preservation of inner invar *) let ?M = "{ { ai \ ai < n" and invAB: "invAB2 A B M ?M" using 3 by auto note invar = 3[THEN conjunct1, THEN conjunct1, unfolded invar2_def] note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast let ?a = "B ! match A a" have a: "a < n \ a \ ?M" using invar by auto have a': "?a \ ?M \ match A ?a = match A a" using invAB match_less_n[OF A] a matched by (metis \_Some ranI) have "?M \ { {a} = { B M) (dom (\ B M))" by (metis (mono_tags) domD inj_onI invAB) have invAB': "invAB (A[B ! match A a := A ! ?a + 1]) (\ (B[match A a := a]) M) ({update2) show ?case using invAM_swap[OF invAM a a' unstab] invAB' invar a' unfolding * by (simp add: insert_absorb \update2) case 2 show ?case using v var_next[OF m M \?M \ { pref_match1 \?a < n\] card by (metis var_def Suc_eq_plus1) next case stab: 3 have *: "\b. b < n \ M!b \ a \ B!b" by (metis invAB ranI \_Some a) show ?case using invAM_next[OF invAM a a' stab] invar * by (simp add: match_def) case 4 show ?case using v var_next[OF m M \?M \ { pref_match1, of a] a card by (metis Suc_eq_plus1 var_def) qed qed qed -subsection \Algorithm 5': single-loop variant\ +subsubsection \Algorithm 5.1: single-loop variant\ definition invar2' where [simp]: "invar2' A B M ai a \ (invAM A ({ invAB2 A B M ({ a \ ai \ ai \ n)" -lemma Gale_Shapley5': "VARS A B M a a' ai b +lemma pres2': +assumes "invar2' A B M ai a" "ai < n" "var A ({ M ! b \ + invar2' A (B[b := a]) (M[b := True]) (ai + 1) (ai + 1) \ var A ({ + (M ! b \ + (P\<^sub>b ! match A a' \ a < a' \ invar2' A1 (B[b := a]) M ai a' \ var A1 ({ + (\ P\<^sub>b ! match A a' \ a < a' \ invar2' A2 B M ai a \ var A2 ({ { ai \ ai < n" and invAB: "invAB2 A B M ?M" + using assms by auto + note invar = assms(1) + note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] + hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast + have a: "a < n \ a \ ?M" using as by (simp) + show ?thesis (is "(\ ?matched \ ?THEN) \ (?matched \ ?ELSE)") + proof (rule; rule) + assume "\ ?matched" + then have nm: "match A a \ match A ` ?M" using invAB unfolding ran_def + apply (clarsimp simp: set_eq_iff) by metis + show ?THEN + proof(rule conjI, goal_cases) + have *: "{ {a} = {\ ?matched\ ** + by (simp add: A a \update1 match_less_n nth_list_update) + case 1 show ?case using invAM' as invAB' * + by (simp add: Suc_le_eq plus_1_eq_Suc) + case 2 show ?case + using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan * ** + unfolding v by (metis lessThan_iff) + qed + next + assume matched: "?matched" + let ?a = "B ! match A a" + have a': "?a \ ?M \ match A ?a = match A a" + using invAB match_less_n[OF A] a matched after by (metis \_Some ranI) + hence "?a < n" "a \ ?a" using a M atLeast0LessThan by auto + have inj_dom: "inj_on (\ B M) (dom (\ B M))" by (metis (mono_tags) domD inj_onI invAB) + show ?ELSE (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") + proof (rule; rule) + assume ?pref + show ?THEN + proof (rule conjI, goal_cases) + have *: "{ {a} = {b match A a \ M!b \ ?a \ B!b" + using invAB a' by force + have invAB': "invAB (A[B ! match A a := A ! ?a + 1]) (\ (B[match A a := a]) M) ({update2) + case 1 show ?case using invAM_swap[OF invAM a a'] \?pref\ invAB invAB' unfolding * + using a' as by simp + case 2 + have "card({?a < n\ a atLeast0LessThan + by (metis Suc_eq_plus1 lessThan_iff var_def after) + qed + next + assume "\ ?pref" + show ?ELSE + proof (rule conjI, goal_cases) + case 1 + have "invAB2 (A[a := A ! a + 1]) B M ?M" using invAB a + by (metis match_def nth_list_update_neq ranI) + thus ?case using invAM_next[OF invAM a a'] \\ ?pref\ invar after + by (meson invar2'_def) + case 2 + show ?case using a v var_next[OF m M _ pref_match1, of a] after + by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) + qed + qed + qed +qed + +lemma Gale_Shapley5_1: "VARS A B M a a' ai b [ai = 0 \ a = 0 \ A = replicate n 0 \ length B = n \ M = replicate n False] WHILE ai < n INV { invar2' A B M ai a } VAR {var A ({ M ! b THEN B[b] := a; M[b] := True; ai := ai + 1; a := ai ELSE a' := B ! b; IF P\<^sub>b ! match A a' \ a < a' THEN B[b] := a; A[a'] := A!a'+1; a := a' ELSE A[a] := A!a+1 FI FI OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case by(auto simp: pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) next case 3 thus ?case using pref_match_stable using atLeast0_lessThan_Suc by force next case (2 v A B M a a' ai b) - let ?M = "{ { ai \ ai < n" and invAB: "invAB2 A B M ?M" - using 2 by auto - note invar = 2[THEN conjunct1,THEN conjunct1] - note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] - hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast - have a: "a < n \ a \ ?M" using as by (simp) - show ?case (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") - proof (rule; rule) - assume ?not_matched - then have nm: "match A a \ match A ` ?M" using invAB unfolding ran_def - apply (clarsimp simp: set_eq_iff) by metis - show ?THEN - proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) - have *: "{ {a} = {?not_matched\ ** - by (simp add: A a \update1 match_less_n nth_list_update) - case 1 show ?case using invAM' as invAB' * - by (simp add: Suc_le_eq plus_1_eq_Suc) - case 2 show ?case - using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan * ** - unfolding v by (metis lessThan_iff) - qed - next - assume matched: "\ ?not_matched" - let ?a = "B ! match A a" - have a': "?a \ ?M \ match A ?a = match A a" - using invAB match_less_n[OF A] a matched by (metis \_Some ranI) - hence "?a < n" "a \ ?a" using a M atLeast0LessThan by auto - show ?ELSE unfolding option.sel - proof (goal_cases) - have inj_dom: "inj_on (\ B M) (dom (\ B M))" by (metis (mono_tags) domD inj_onI invAB) - case 1 - show ?case (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") - proof (rule; rule) - assume ?pref - show ?THEN - proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) - have *: "{ {a} = {b match A a \ M!b \ ?a \ B!b" - using invAB a' by force - have invAB': "invAB (A[B ! match A a := A ! ?a + 1]) (\ (B[match A a := a]) M) ({update2) - case 1 show ?case using invAM_swap[OF invAM a a' \?pref\] invAB invAB' unfolding * - using a' as by simp - case 2 - have "card({?a < n\ a atLeast0LessThan - by (metis Suc_eq_plus1 lessThan_iff var_def) - qed - next - assume "\ ?pref" - show ?ELSE - proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) - case 1 - have "invAB2 (A[a := A ! a + 1]) B M ?M" using invAB a - by (metis match_def nth_list_update_neq ranI) - thus ?case using invAM_next[OF invAM a a' \\ ?pref\] using "2" - by (meson invar2'_def) - case 2 - show ?case using a v var_next[OF m M _ pref_match1, of a] - by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) - qed - qed - qed - qed + thus ?case using pres2' + by (simp only:mem_Collect_eq prod.case) blast qed subsection \Algorithm 6: replace \P\<^sub>b\ by ranking \R\<^sub>b\\ lemma inner_to_outer: assumes inv: "invar2 A B M ai a \ b = match A a" and not_b: "\ M ! b" shows "invar1 A (B[b := a]) (M[b := True]) (ai+1)" proof - note invAM = inv[unfolded invar2_def, THEN conjunct1,THEN conjunct1] have *: "{ match A a \ match A ` ({ match A ` ({update1 insert_absorb nth_list_update) qed lemma inner_pres: assumes R: "\ba1a2 P\<^sub>b ! b \ a1 < a2" and inv: "invar2 A B M ai a" and m: "M ! b" and v: "var A { invar2 A1 (B[b:=a]) M ai a' \ var A1 { (\ r ! a < r ! a' \ invar2 A2 B M ai a \ var A2 { { B M) ?M" and mat: "matching A ?M" and pref_match: "pref_match A ?M" and as: "a \ ai \ ai < n" using inv' by auto note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast let ?a = "B ! match A a" have a: "a < n \ a \ ?M" using inv by auto have a': "?a \ ?M \ match A ?a = match A a" using invAB match_less_n[OF A] a m inv by (metis \_Some ranI \b = _\) have "?M \ { {a} = {b ! match A a' \ a < a'" using R a a' as P\<^sub>b_set P\<^sub>a_set match_less_n[OF A] n_def length_P\<^sub>b R by (simp) have inj_dom: "inj_on (\ B M) (dom (\ B M))" by (metis (mono_tags) domD inj_onI invAB) have invAB': "invAB A1 (\ (B[match A a := a]) M) ({update2 inv') show ?case using invAM_swap[OF invAM a a'] unstab invAB' inv a' - unfolding * by (simp add: insert_absorb \update2) + unfolding * by (simp) next case 2 - show ?case using v var_next[OF mat M \?M \ { pref_match1 \?a < n\] card assms(5,9) + show ?case using v var_next[OF mat M \?M \ { pref_match1 \?a < n\] card assms(5,7,9) by (metis Suc_eq_plus1 var_def) next have *: "\b. b < n \ M!b \ a \ B!b" by (metis invAB ranI \_Some a) case 3 hence unstab: "\ P\<^sub>b ! match A a' \ a < a'" using R a a' as P\<^sub>b_set P\<^sub>a_set match_less_n[OF A] n_def length_P\<^sub>b by (simp add: ranking_iff_pref) then show ?case using invAM_next[OF invAM a a'] 3 inv * by (simp add: match_def) next case 4 show ?case using v var_next[OF mat M \?M \ { pref_match1, of a] a card assms(6) by (metis Suc_eq_plus1 var_def) qed qed lemma Gale_Shapley6: assumes "R\<^sub>b = map ranking P\<^sub>b" shows "VARS A B M ai a a' b r [ai = 0 \ A = replicate n 0 \ length B = n \ M = replicate n False] WHILE ai < n INV { invar1 A B M ai } VAR {z = n - ai} DO a := ai; b := match A a; WHILE M ! b INV { invar2 A B M ai a \ b = match A a \ z = n-ai } VAR {var A {b ! match A a'; IF r ! a < r ! a' THEN B[b] := a; A[a'] := A!a'+1; a := a' ELSE A[a] := A!a+1 FI; b := match A a OD; B[b] := a; M[b] := True; ai := ai+1 OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case (* outer invar holds initially *) by(auto simp: stable_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) next case 2 (* outer invar and b ibplies inner invar *) thus ?case by (auto simp: atLeastLessThanSuc_atLeastAtMost simp flip: atLeastLessThan_eq_atLeastAtMost_diff) next case 3 (* preservation of inner invar *) have R: "\ba1a2b ! b ! a1 < R\<^sub>b ! b ! a2 \ P\<^sub>b ! b \ a1 < a2" by (simp add: P\<^sub>b_set \R\<^sub>b = _\ length_P\<^sub>b ranking_iff_pref) show ?case proof (simp only: mem_Collect_eq prod.case, goal_cases) case 1 show ?case using inner_pres[OF R _ _ refl refl refl] 3 by blast qed next case 4 (* inner invar' and not b ibplies outer invar *) show ?case proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case using 4 inner_to_outer by blast next case 2 thus ?case using 4 by auto qed next case 5 (* outer invar and not b ibplies post *) thus ?case using pref_match_stable unfolding invAM_def invar1_def by(metis le_neq_implies_less) qed text \A version where the inner variant does not depend on variables changed in the outer loop. Thus the inner variant is an upper bound on the number of executions of the inner loop test/body.\ lemma var0_next2: assumes "wf (A[a' := A ! a' + 1])" "a' < n" shows "var0 (A[a' := A ! a' + 1]) {a n^2" using sumA_ub[OF assms(1)] 0 by (simp add: power2_eq_square algebra_simps le_diff_conv2) have "(\aaba1a2b ! b ! a1 < R\<^sub>b ! b ! a2 \ P\<^sub>b ! b \ a1 < a2" and inv: "invar2 A B M ai a" and m: "M ! b" and v: "var0 A {b ! match A a'" "b = match A a" shows "(r ! a < r ! a' \ invar2 A1 (B[b:=a]) M ai a' \ var0 A1 { (\ r ! a < r ! a' \ invar2 A2 B M ai a \ var0 A2 { { B M) ?M" and mat: "matching A ?M" and pref_match: "pref_match A ?M" and as: "a \ ai \ ai < n" using inv' by auto note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast let ?a = "B ! match A a" have a: "a < n \ a \ ?M" using inv by auto have a': "?a \ ?M \ match A ?a = match A a" using invAB match_less_n[OF A] a m inv by (metis \_Some ranI \b = _\) have "?M \ { {a} = {b ! match A a' \ a < a'" using R a a' as P\<^sub>b_set P\<^sub>a_set match_less_n[OF A] n_def length_P\<^sub>b R by (simp) have inj_dom: "inj_on (\ B M) (dom (\ B M))" by (metis (mono_tags) domD inj_onI invAB) have invAB': "invAB A1 (\ (B[match A a := a]) M) ({update2 inv') show ?case using invAM_swap[OF invAM a a'] unstab invAB' inv a' unfolding * by (simp add: insert_absorb \update2) next case 2 hence unstab: "P\<^sub>b ! match A a' \ a < a'" using R a a' as P\<^sub>b_set P\<^sub>a_set match_less_n[OF A] n_def length_P\<^sub>b R by (simp) from invAM_swap[OF invAM a a'] unstab have wf: "wf (A[a' := A ! a' + 1])" by auto show ?case using v var0_next2[OF wf] using \B ! match A a < n\ assms(5,7,9) by blast next have *: "\b. b < n \ M!b \ a \ B!b" by (metis invAB ranI \_Some a) case 3 hence unstab: "\ P\<^sub>b ! match A a' \ a < a'" using R a a' as P\<^sub>b_set P\<^sub>a_set match_less_n[OF A] n_def length_P\<^sub>b by (simp add: ranking_iff_pref) then show ?case using invAM_next[OF invAM a a'] 3 inv * by (simp add: match_def) next case 4 hence unstab: "\ P\<^sub>b ! match A a' \ a < a'" using R a a' as P\<^sub>b_set P\<^sub>a_set match_less_n[OF A] n_def length_P\<^sub>b by (simp add: ranking_iff_pref) from invAM_next[OF invAM a a'] unstab have wf: "wf (A[a := A ! a + 1])" by auto show ?case using v var0_next2[OF wf] a using assms(6) by presburger qed qed lemma Gale_Shapley6': assumes "R\<^sub>b = map ranking P\<^sub>b" shows "VARS A B M ai a a' b r [ai = 0 \ A = replicate n 0 \ length B = n \ M = replicate n False] WHILE ai < n INV { invar1 A B M ai } VAR {z = n - ai} DO a := ai; b := match A a; WHILE M ! b INV { invar2 A B M ai a \ b = match A a \ z = n-ai } VAR {var0 A {b ! match A a'; IF r ! a < r ! a' THEN B[b] := a; A[a'] := A!a'+1; a := a' ELSE A[a] := A!a+1 FI; b := match A a OD; B[b] := a; M[b] := True; ai := ai+1 OD [matching A { stable A { opti\<^sub>a A]" proof (vcg_tc, goal_cases) case 1 thus ?case (* outer invar holds initially *) by(auto simp: stable_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) next case 2 (* outer invar and b ibplies inner invar *) thus ?case by (auto simp: atLeastLessThanSuc_atLeastAtMost simp flip: atLeastLessThan_eq_atLeastAtMost_diff) next case 3 (* preservation of inner invar *) have R: "\ba1a2b ! b ! a1 < R\<^sub>b ! b ! a2 \ P\<^sub>b ! b \ a1 < a2" by (simp add: P\<^sub>b_set \R\<^sub>b = _\ length_P\<^sub>b ranking_iff_pref) show ?case proof (simp only: mem_Collect_eq prod.case, goal_cases) case 1 show ?case using inner_pres2[OF R _ _ refl refl refl] 3 by blast qed next case 4 (* inner invar' and not b ibplies outer invar *) show ?case proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) case 1 show ?case using 4 inner_to_outer by blast next case 2 thus ?case using 4 by auto qed next case 5 (* outer invar and not b ibplies post *) thus ?case using pref_match_stable unfolding invAM_def invar1_def by(metis le_neq_implies_less) qed +subsubsection \Algorithm 6.1: single-loop variant\ + +lemma R_iff_P: +assumes "R\<^sub>b = map ranking P\<^sub>b" "invar2' A B M ai a" "ai < n" "M ! match A a" +shows "(R\<^sub>b ! match A (B ! match A a) ! a < R\<^sub>b ! match A (B ! match A a) ! (B ! match A a)) = + (P\<^sub>b ! match A (B ! match A a) \ a < B ! match A a)" +proof - + have R: "\ba1a2b ! b ! a1 < R\<^sub>b ! b ! a2 \ P\<^sub>b ! b \ a1 < a2" + by (simp add: P\<^sub>b_set \R\<^sub>b = _\ length_P\<^sub>b ranking_iff_pref) + let ?M = "{ { ?M" + using invAB match_less_n[OF A] as \M!match A a\ by (metis \_Some ranI) + hence "B ! match A a < n" using M by auto + thus ?thesis using assms R match_less_n by auto +qed + + +lemma Gale_Shapley6_1: +assumes "R\<^sub>b = map ranking P\<^sub>b" +shows "VARS A B M a a' ai b r + [ai = 0 \ a = 0 \ A = replicate n 0 \ length B = n \ M = replicate n False] + WHILE ai < n + INV { invar2' A B M ai a } + VAR {var A ({ M ! b + THEN B[b] := a; M[b] := True; ai := ai + 1; a := ai + ELSE a' := B ! b; r := R\<^sub>b ! match A a'; + IF r ! a < r ! a' + THEN B[b] := a; A[a'] := A!a'+1; a := a' + ELSE A[a] := A!a+1 + FI + FI + OD + [matching A { stable A { opti\<^sub>a A]" +proof (vcg_tc, goal_cases) + case 1 thus ?case + by(auto simp: pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) +next + case 3 thus ?case using pref_match_stable atLeast0_lessThan_Suc by force +next + case (2 v A B M a a' ai) + have R': "M ! match A a \ + (R\<^sub>b ! match A (B ! match A a) ! a < R\<^sub>b ! match A (B ! match A a) ! (B ! match A a)) = + (P\<^sub>b ! match A (B ! match A a) \ a < B ! match A a)" + using R_iff_P 2 assms by blast + show ?case + apply(simp only:mem_Collect_eq prod.case) + using 2 R' pres2'[of A B M ai a] by presburger +qed + +(* TODO: rm? *) +lemma Gale_Shapley6_1_explicit: +assumes "R\<^sub>b = map ranking P\<^sub>b" +shows "VARS A B M a a' ai b r + [ai = 0 \ a = 0 \ A = replicate n 0 \ length B = n \ M = replicate n False] + WHILE ai < n + INV { invar2' A B M ai a } + VAR {var A ({ M ! b + THEN B[b] := a; M[b] := True; ai := ai + 1; a := ai + ELSE a' := B ! b; r := R\<^sub>b ! match A a'; + IF r ! a < r ! a' + THEN B[b] := a; A[a'] := A!a'+1; a := a' + ELSE A[a] := A!a+1 + FI + FI + OD + [matching A { stable A { opti\<^sub>a A]" +proof (vcg_tc, goal_cases) + case 1 thus ?case + by(auto simp: pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) +next + case 3 thus ?case using pref_match_stable + using atLeast0_lessThan_Suc by force +next + case (2 v A B M a a' ai b) + let ?M = "{ { ai \ ai < n" and invAB: "invAB2 A B M ?M" + using 2 by auto + note invar = 2[THEN conjunct1,THEN conjunct1] + note pref_match' = pref_match[THEN pref_match'_iff[OF A, THEN iffD2]] + hence pref_match1: "\a match A ` ?M" unfolding pref_match'_def by blast + have a: "a < n \ a \ ?M" using as by (simp) + show ?case (is "(?not_matched \ ?THEN) \ (\ ?not_matched \ ?ELSE)") + proof (rule; rule) + assume ?not_matched + then have nm: "match A a \ match A ` ?M" using invAB unfolding ran_def + apply (clarsimp simp: set_eq_iff) by metis + show ?THEN + proof(simp only:mem_Collect_eq prod.case, rule conjI, goal_cases) + have *: "{ {a} = {?not_matched\ ** + by (simp add: A a \update1 match_less_n nth_list_update) + case 1 show ?case using invAM' as invAB' * + by (simp add: Suc_le_eq plus_1_eq_Suc) + case 2 show ?case + using var_match[OF m M _ pref_match1, of a] a atLeast0LessThan * ** + unfolding v by (metis lessThan_iff) + qed + next + assume matched: "\ ?not_matched" + let ?a = "B ! match A a" + have a': "?a \ ?M \ match A ?a = match A a" + using invAB match_less_n[OF A] a matched by (metis \_Some ranI) + hence "?a < n" "a \ ?a" using a M atLeast0LessThan by auto + have inj_dom: "inj_on (\ B M) (dom (\ B M))" by (metis (mono_tags) domD inj_onI invAB) + show ?ELSE (is "(?pref \ ?THEN) \ (\ ?pref \ ?ELSE)") + proof (rule; rule) + assume ?pref + show ?THEN + proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) + have *: "{ {a} = {b match A a \ M!b \ ?a \ B!b" + using invAB a' by force + have invAB': "invAB (A[B ! match A a := A ! ?a + 1]) (\ (B[match A a := a]) M) ({update2) + have pref: "P\<^sub>b ! match A ?a \ a < ?a" using A P\<^sub>b_set \?a < n\ \?pref\ a assms length_P\<^sub>b + by(auto simp: match_less_n ranking_iff_pref) + case 1 show ?case (* changed *) + using invAM_swap[OF invAM a a' pref] invAB invAB' a' as unfolding * + by (simp add: match_less_n ranking_iff_pref) + case 2 + have "card({?a < n\ a atLeast0LessThan + by (metis Suc_eq_plus1 lessThan_iff var_def) + qed + next + assume "\ ?pref" + show ?ELSE + proof (simp only: mem_Collect_eq prod.case, rule conjI, goal_cases) + case 1 + have "invAB2 (A[a := A ! a + 1]) B M ?M" using invAB a + by (metis match_def nth_list_update_neq ranI) + thus ?case using invAM_next[OF invAM a a'] \\ ?pref\ \B ! match A a < n\ P\<^sub>b_set 2 assms + by (simp add: invar2'_def length_P\<^sub>b match_less_n ranking_iff_pref) (* changed *) + case 2 + show ?case using a v var_next[OF m M _ pref_match1, of a] + by (metis Suc_eq_plus1 atLeast0LessThan lessThan_iff) + qed + qed + qed +qed + end subsection \Functional implementation\ definition "gs_inner P\<^sub>a R\<^sub>b M = while (\(A,B,a,b). M!b) (\(A,B,a,b). let a' = B ! b; r = R\<^sub>b ! (P\<^sub>a ! a' ! (A ! a')) in let (A, B, a) = if r ! a < r ! a' then (A[a' := A!a' + 1], B[b := a], a') else (A[a := A!a + 1], B, a) in (A, B, a, P\<^sub>a ! a ! (A ! a)))" definition "gs n P\<^sub>a R\<^sub>b = while (\(A,B,M,ai). ai < n) (\(A,B,M,ai). let (A,B,a,b) = gs_inner P\<^sub>a R\<^sub>b M (A, B, ai, P\<^sub>a ! ai ! (A ! ai)) in (A, B[b:=a], M[b:=True], ai+1)) (replicate n 0, replicate n 0, replicate n False,0)" +definition +"gs1 n P\<^sub>a R\<^sub>b = + while (\(A,B,M,ai,a). ai < n) + (\(A,B,M,ai,a). + let b = P\<^sub>a ! a ! (A ! a) in + if \ M ! b + then (A, B[b := a], M[b := True], ai+1, ai+1) + else let a' = B ! b; r = R\<^sub>b ! (P\<^sub>a ! a' ! (A ! a')) in + if r ! a < r ! a' + then (A[a' := A!a'+1], B[b := a], M, ai, a') + else (A[a := A!a+1], B, M, ai, a)) + (replicate n 0, replicate n 0, replicate n False, 0, 0)" + context Pref begin lemma gs_inner: assumes "R\<^sub>b = map ranking P\<^sub>b" assumes "invar2 A B M ai a" "b = match A a" shows "gs_inner P\<^sub>a R\<^sub>b M (A, B, a, b) = (A',B',a',b') \ invar1 A' (B'[b' := a']) (M[b' := True]) (ai+1)" unfolding gs_inner_def proof(rule while_rule2[where P = "\(A,B,a,b). invar2 A B M ai a \ b = match A a" and r = "measure (%(A, B, a, b). Pref.var P\<^sub>a A {ba1a2b ! b ! a1 < R\<^sub>b ! b ! a2 \ P\<^sub>b ! b \ a1 < a2" by (simp add: P\<^sub>b_set \R\<^sub>b = _\ length_P\<^sub>b ranking_iff_pref) show ?case proof(rule, goal_cases) case 1 show ?case using inv apply(simp only: s prod.case Let_def split: if_split) using inner_pres[OF R _ _ refl refl refl refl refl, of A B M ai a b] unfolding invar2_def match_def by presburger case 2 show ?case using inv apply(simp only: s prod.case Let_def in_measure split: if_split) using inner_pres[OF R _ _ refl refl refl refl refl, of A B M ai a b] unfolding invar2_def match_def by presburger qed next case 3 show ?case proof (rule, goal_cases) case 1 show ?case by(rule inner_to_outer[OF 3[unfolded 1 prod.case]]) qed next case 4 show ?case by simp qed lemma gs: assumes "R\<^sub>b = map ranking P\<^sub>b" shows "gs n P\<^sub>a R\<^sub>b = (A,BMai) \ matching A { stable A { opti\<^sub>a A" unfolding gs_def proof(rule while_rule2[where P = "\(A,B,M,ai). invar1 A B M ai" and r = "measure(\(A,B,M,ai). n - ai)"], goal_cases) case 1 show ?case by(auto simp: stable_def pref_match_def P\<^sub>a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) next case (2 s) obtain A B M ai where s: "s = (A, B, M, ai)" using prod_cases4 by blast have 1: "invar2 A B M ai ai" using 2 s by (auto simp: atLeastLessThanSuc_atLeastAtMost simp flip: atLeastLessThan_eq_atLeastAtMost_diff) show ?case using 2 s gs_inner[OF \R\<^sub>b = _ \ 1] by (auto simp: match_def simp del: invar1_def split: prod.split) next case 3 thus ?case using pref_match_stable by auto next case 4 show ?case by simp qed + +lemma gs1: assumes "R\<^sub>b = map ranking P\<^sub>b" +shows "gs1 n P\<^sub>a R\<^sub>b = (A,BMaia) \ matching A { stable A { opti\<^sub>a A" +unfolding gs1_def +proof(rule while_rule2[where P = "\(A,B,M,ai,a). invar2' A B M ai a" + and r = "measure (%(A, B, M, ai, a). Pref.var P\<^sub>a A ({a_set card_distinct match_def index_nth_id prefers_def opti\<^sub>a_def \_def cong: conj_cong) +next + case (2 s) + obtain A B M ai a where s: "s = (A, B, M, ai, a)" + using prod_cases5 by blast + hence 1: "invar2' A B M ai a" "ai < n" using 2 by (simp_all) + have R': "M ! match A a \ + (R\<^sub>b ! match A (B ! match A a) ! a < R\<^sub>b ! match A (B ! match A a) ! (B ! match A a)) = + (P\<^sub>b ! match A (B ! match A a) \ a < B ! match A a)" + using R_iff_P[OF assms 1] by linarith + show ?case + using 1 R' pres2'[OF 1] + apply(simp only: s mem_Collect_eq prod.case Let_def in_measure match_def split: if_split) + by blast +next + case (3 s) + obtain B M ai a where "BMaia = (B, M, ai, a)" + using prod_cases4 by blast + with 3 show ?case + using pref_match_stable atLeast0_lessThan_Suc by force +next + case 4 + show ?case by simp +qed + + end subsection \Executable functional Code\ definition "Gale_Shapley P\<^sub>a P\<^sub>b = (if Pref P\<^sub>a P\<^sub>b then Some (fst (gs (length P\<^sub>a) P\<^sub>a (map ranking P\<^sub>b))) else None)" theorem gs: "\ Pref P\<^sub>a P\<^sub>b; n = length P\<^sub>a \ \ \A. Gale_Shapley P\<^sub>a P\<^sub>b = Some(A) \ Pref.matching P\<^sub>a A { Pref.stable P\<^sub>a P\<^sub>b A { Pref.opti\<^sub>a P\<^sub>a P\<^sub>b A" unfolding Gale_Shapley_def using Pref.gs by (metis fst_conv surj_pair) declare Pref_def [code] text \Two examples from Gusfield and Irving:\ lemma "Gale_Shapley [[3,0,1,2], [1,2,0,3], [1,3,2,0], [2,0,3,1]] [[3,0,2,1], [0,2,1,3], [0,1,2,3], [3,0,2,1]] = Some[0,1,0,1]" by eval lemma "Gale_Shapley [[4,6,0,1,5,7,3,2], [1,2,6,4,3,0,7,5], [7,4,0,3,5,1,2,6], [2,1,6,3,0,5,7,4], [6,1,4,0,2,5,7,3], [0,5,6,4,7,3,1,2], [1,4,6,5,2,3,7,0], [2,7,3,4,6,1,5,0]] [[4,2,6,5,0,1,7,3], [7,5,2,4,6,1,0,3], [0,4,5,1,3,7,6,2], [7,6,2,1,3,0,4,5], [5,3,6,2,7,0,1,4], [1,7,4,2,3,5,6,0], [6,4,1,0,7,5,3,2], [6,3,0,4,1,2,5,7]] = Some [0, 1, 0, 5, 0, 0, 0, 2]" by eval end \ No newline at end of file