# HG changeset patch # User Manuel Eberl # Date 1748946178 -7200 # Node ID 71304514891e1587e19f431e2e722e639841de8a # Parent 06aac7eaec295a03d6378fc444ec97e24fd46e08 HOL-Combinatorics: more lemmas about permutations diff -r 06aac7eaec29 -r 71304514891e src/HOL/Combinatorics/Permutations.thy --- a/src/HOL/Combinatorics/Permutations.thy Sun Jun 01 20:01:22 2025 +0200 +++ b/src/HOL/Combinatorics/Permutations.thy Tue Jun 03 12:22:58 2025 +0200 @@ -1,4 +1,6 @@ (* Author: Amine Chaieb, University of Cambridge + + With various additions by Manuel Eberl *) section \Permutations, both general and specifically on finite sets.\ @@ -43,6 +45,33 @@ by (simp add: permutes_def) qed +lemma inj_imp_permutes: + assumes i: "inj_on f S" and fin: "finite S" + and fS: "\x. x \ S \ f x \ S" + and f: "\i. i \ S \ f i = i" + shows "f permutes S" + unfolding permutes_def +proof (intro conjI allI impI, rule f) + fix y + from endo_inj_surj[OF fin _ i] fS have fs: "f ` S = S" by auto + show "\!x. f x = y" + proof (cases "y \ S") + case False + thus ?thesis by (intro ex1I[of _ y], insert fS f) force+ + next + case True + with fs obtain x where x: "x \ S" and fx: "f x = y" by force + show ?thesis + proof (rule ex1I, rule fx) + fix x' + assume fx': "f x' = y" + with True f[of x'] have "x' \ S" by metis + from inj_onD[OF i fx[folded fx'] x this] + show "x' = x" by simp + qed + qed +qed + context fixes p :: \'a \ 'a\ and S :: \'a set\ assumes perm: \p permutes S\ @@ -184,6 +213,10 @@ lemma permutes_swap_id: "a \ S \ b \ S \ transpose a b permutes S" by (rule bij_imp_permutes) (auto intro: transpose_apply_other) +lemma permutes_altdef: "p permutes A \ bij_betw p A A \ {x. p x \ x} \ A" + using permutes_not_in[of p A] + by (auto simp: permutes_imp_bij intro!: bij_imp_permutes) + lemma permutes_superset: \p permutes T\ if \p permutes S\ \\x. x \ S - T \ p x = x\ proof - @@ -296,7 +329,252 @@ by (simp add: bij_vimage_eq_inv_image permutes_bij permutes_image[OF permutes_inv]) -subsection \Mapping permutations with bijections\ +subsection \Restricting a permutation to a subset\ + +definition restrict_id :: "('a \ 'a) \ 'a set \ 'a \ 'a" + where "restrict_id f A = (\x. if x \ A then f x else x)" + +lemma restrict_id_cong [cong]: + assumes "\x. x \ A \ f x = g x" "A = B" + shows "restrict_id f A = restrict_id g B" + using assms unfolding restrict_id_def by auto + +lemma restrict_id_cong': + assumes "x \ A \ f x = g x" "A = B" + shows "restrict_id f A x = restrict_id g B x" + using assms unfolding restrict_id_def by auto + +lemma restrict_id_simps [simp]: + "x \ A \ restrict_id f A x = f x" + "x \ A \ restrict_id f A x = x" + by (auto simp: restrict_id_def) + +lemma bij_betw_restrict_id: + assumes "bij_betw f A A" "A \ B" + shows "bij_betw (restrict_id f A) B B" +proof - + have "bij_betw (restrict_id f A) (A \ (B - A)) (A \ (B - A))" + unfolding restrict_id_def + by (rule bij_betw_disjoint_Un) (use assms in \auto intro: bij_betwI\) + also have "A \ (B - A) = B" + using assms(2) by blast + finally show ?thesis . +qed + +lemma permutes_restrict_id: + assumes "bij_betw f A A" + shows "restrict_id f A permutes A" + by (intro bij_imp_permutes bij_betw_restrict_id assms) auto + + +subsection \Mapping a permutation\ + +definition map_permutation :: "'a set \ ('a \ 'b) \ ('a \ 'a) \ 'b \ 'b" where + "map_permutation A f p = restrict_id (f \ p \ inv_into A f) (f ` A)" + +lemma map_permutation_cong_strong: + assumes "A = B" "\x. x \ A \ f x = g x" "\x. x \ A \ p x = q x" + assumes "p ` A \ A" "inj_on f A" + shows "map_permutation A f p = map_permutation B g q" +proof - + have fg: "f x = g y" if "x \ A" "x = y" for x y + using assms(2) that by simp + have pq: "p x = q y" if "x \ A" "x = y" for x y + using assms(3) that by simp + have p: "p x \ A" if "x \ A" for x + using assms(4) that by blast + have inv: "inv_into A f x = inv_into B g y" if "x \ f ` A" "x = y" for x y + proof - + from that obtain u where u: "u \ A" "x = f u" + by blast + have "inv_into A f (f u) = inv_into A g (f u)" + using \inj_on f A\ u(1) by (metis assms(2) inj_on_cong inv_into_f_f) + thus ?thesis + using u \x = y\ \A = B\ by simp + qed + + show ?thesis + unfolding map_permutation_def o_def + by (intro restrict_id_cong image_cong fg pq inv_into_into p inv) (auto simp: \A = B\) +qed + +lemma map_permutation_cong: + assumes "inj_on f A" "p permutes A" + assumes "A = B" "\x. x \ A \ f x = g x" "\x. x \ A \ p x = q x" + shows "map_permutation A f p = map_permutation B g q" +proof (intro map_permutation_cong_strong assms) + show "p ` A \ A" + using \p permutes A\ by (simp add: permutes_image) +qed auto + +lemma inv_into_id [simp]: "x \ A \ inv_into A id x = x" + by (metis f_inv_into_f id_apply image_eqI) + +lemma inv_into_ident [simp]: "x \ A \ inv_into A (\x. x) x = x" + by (metis f_inv_into_f image_eqI) + +lemma map_permutation_id [simp]: "p permutes A \ map_permutation A id p = p" + by (auto simp: fun_eq_iff map_permutation_def restrict_id_def permutes_not_in) + +lemma map_permutation_ident [simp]: "p permutes A \ map_permutation A (\x. x) p = p" + by (auto simp: fun_eq_iff map_permutation_def restrict_id_def permutes_not_in) + +lemma map_permutation_id': "inj_on f A \ map_permutation A f id = id" + unfolding map_permutation_def by (auto simp: restrict_id_def fun_eq_iff) + +lemma map_permutation_ident': "inj_on f A \ map_permutation A f (\x. x) = (\x. x)" + unfolding map_permutation_def by (auto simp: restrict_id_def fun_eq_iff) + +lemma map_permutation_permutes: + assumes "bij_betw f A B" "p permutes A" + shows "map_permutation A f p permutes B" +proof (rule bij_imp_permutes) + have f_A: "f ` A = B" + using assms(1) by (auto simp: bij_betw_def) + from assms(2) have "bij_betw p A A" + by (simp add: permutes_imp_bij) + show "bij_betw (map_permutation A f p) B B" + unfolding map_permutation_def f_A + by (rule bij_betw_restrict_id bij_betw_trans bij_betw_inv_into assms(1) + permutes_imp_bij[OF assms(2)] order.refl)+ + show "map_permutation A f p x = x" if "x \ B" for x + using that unfolding map_permutation_def f_A by simp +qed + +lemma map_permutation_compose: + fixes f :: "'a \ 'b" and g :: "'b \ 'c" + assumes "bij_betw f A B" "inj_on g B" + shows "map_permutation B g (map_permutation A f p) = map_permutation A (g \ f) p" +proof + fix c :: 'c + have bij_g: "bij_betw g B (g ` B)" + using \inj_on g B\ unfolding bij_betw_def by blast + have [simp]: "f x = f y \ x = y" if "x \ A" "y \ A" for x y + using assms(1) that by (auto simp: bij_betw_def inj_on_def) + have [simp]: "g x = g y \ x = y" if "x \ B" "y \ B" for x y + using assms(2) that by (auto simp: bij_betw_def inj_on_def) + show "map_permutation B g (map_permutation A f p) c = map_permutation A (g \ f) p c" + proof (cases "c \ g ` B") + case c: True + then obtain a where a: "a \ A" "c = g (f a)" + using assms(1,2) unfolding bij_betw_def by auto + have "map_permutation B g (map_permutation A f p) c = g (f (p a))" + using a assms by (auto simp: map_permutation_def restrict_id_def bij_betw_def) + also have "\ = map_permutation A (g \ f) p c" + using a bij_betw_inv_into_left[OF bij_betw_trans[OF assms(1) bij_g]] + by (auto simp: map_permutation_def restrict_id_def bij_betw_def) + finally show ?thesis . + next + case c: False + thus ?thesis using assms + by (auto simp: map_permutation_def bij_betw_def restrict_id_def) + qed +qed + +lemma map_permutation_compose_inv: + assumes "bij_betw f A B" "p permutes A" "\x. x \ A \ g (f x) = x" + shows "map_permutation B g (map_permutation A f p) = p" +proof - + have "inj_on g B" + proof + fix x y assume "x \ B" "y \ B" "g x = g y" + then obtain x' y' where *: "x' \ A" "y' : A" "x = f x'" "y = f y'" + using assms(1) unfolding bij_betw_def by blast + thus "x = y" + using assms(3)[of x'] assms(3)[of y'] \g x = g y\ by simp + qed + + have "map_permutation B g (map_permutation A f p) = map_permutation A (g \ f) p" + by (rule map_permutation_compose) (use assms \inj_on g B\ in auto) + also have "\ = map_permutation A id p" + by (intro map_permutation_cong assms comp_inj_on) + (use \inj_on g B\ assms(1,3) in \auto simp: bij_betw_def\) + also have "\ = p" + by (rule map_permutation_id) fact + finally show ?thesis . +qed + +lemma map_permutation_apply: + assumes "inj_on f A" "x \ A" + shows "map_permutation A f h (f x) = f (h x)" + using assms by (auto simp: map_permutation_def inj_on_def) + +lemma map_permutation_compose': + fixes f :: "'a \ 'b" + assumes "inj_on f A" "q permutes A" + shows "map_permutation A f (p \ q) = map_permutation A f p \ map_permutation A f q" +proof + fix y :: 'b + show "map_permutation A f (p \ q) y = (map_permutation A f p \ map_permutation A f q) y" + proof (cases "y \ f ` A") + case True + then obtain x where x: "x \ A" "y = f x" + by blast + have "map_permutation A f (p \ q) y = f (p (q x))" + unfolding x(2) by (subst map_permutation_apply) (use assms x in auto) + also have "\ = (map_permutation A f p \ map_permutation A f q) y" + unfolding x o_apply using x(1) assms + by (simp add: map_permutation_apply permutes_in_image) + finally show ?thesis . + next + case False + thus ?thesis + using False by (simp add: map_permutation_def) + qed +qed + +lemma map_permutation_transpose: + assumes "inj_on f A" "a \ A" "b \ A" + shows "map_permutation A f (Transposition.transpose a b) = Transposition.transpose (f a) (f b)" +proof + fix y :: 'b + show "map_permutation A f (Transposition.transpose a b) y = Transposition.transpose (f a) (f b) y" + proof (cases "y \ f ` A") + case False + hence "map_permutation A f (Transposition.transpose a b) y = y" + unfolding map_permutation_def by (intro restrict_id_simps) + moreover have "Transposition.transpose (f a) (f b) y = y" + using False assms by (intro transpose_apply_other) auto + ultimately show ?thesis + by simp + next + case True + then obtain x where x: "x \ A" "y = f x" + by blast + have "map_permutation A f (Transposition.transpose a b) y = + f (Transposition.transpose a b x)" + unfolding x by (subst map_permutation_apply) (use x assms in auto) + also have "\ = Transposition.transpose (f a) (f b) y" + using assms(2,3) x + by (auto simp: Transposition.transpose_def inj_on_eq_iff[OF assms(1)]) + finally show ?thesis . + qed +qed + +lemma map_permutation_permutes_iff: + assumes "bij_betw f A B" "p ` A \ A" "\x. x \ A \ p x = x" + shows "map_permutation A f p permutes B \ p permutes A" +proof + assume "p permutes A" + thus "map_permutation A f p permutes B" + by (intro map_permutation_permutes assms) +next + assume *: "map_permutation A f p permutes B" + hence "map_permutation B (inv_into A f) (map_permutation A f p) permutes A" + by (rule map_permutation_permutes[OF bij_betw_inv_into[OF assms(1)]]) + also have "map_permutation B (inv_into A f) (map_permutation A f p) = + map_permutation A (inv_into A f \ f) p" + by (rule map_permutation_compose[OF _ inj_on_inv_into]) + (use assms in \auto simp: bij_betw_def\) + also have "\ = map_permutation A id p" + unfolding o_def id_def + by (rule sym, intro map_permutation_cong_strong inv_into_f_f[symmetric] + assms(2) bij_betw_imp_inj_on[OF assms(1)]) auto + also have "\ = p" + unfolding map_permutation_def using assms(3) + by (auto simp: restrict_id_def fun_eq_iff split: if_splits) + finally show "p permutes A" . +qed lemma bij_betw_permutations: assumes "bij_betw f A B" @@ -495,56 +773,19 @@ shows "finite {p. p permutes S}" using card_permutations[OF refl assms] by (auto intro: card_ge_0_finite) - -subsection \Hence a sort of induction principle composing by swaps\ - -lemma permutes_induct [consumes 2, case_names id swap]: - \P p\ if \p permutes S\ \finite S\ - and id: \P id\ - and swap: \\a b p. a \ S \ b \ S \ p permutes S \ P p \ P (transpose a b \ p)\ -using \finite S\ \p permutes S\ swap proof (induction S arbitrary: p) - case empty - with id show ?case - by (simp only: permutes_empty) -next - case (insert x S p) - define q where \q = transpose x (p x) \ p\ - then have swap_q: \transpose x (p x) \ q = p\ - by (simp add: o_assoc) - from \p permutes insert x S\ have \q permutes S\ - by (simp add: q_def permutes_insert_lemma) - then have \q permutes insert x S\ - by (simp add: permutes_imp_permutes_insert) - from \q permutes S\ have \P q\ - by (auto intro: insert.IH insert.prems(2) permutes_imp_permutes_insert) - have \x \ insert x S\ - by simp - moreover from \p permutes insert x S\ have \p x \ insert x S\ - using permutes_in_image [of p \insert x S\ x] by simp - ultimately have \P (transpose x (p x) \ q)\ - using \q permutes insert x S\ \P q\ - by (rule insert.prems(2)) - then show ?case - by (simp add: swap_q) -qed - -lemma permutes_rev_induct [consumes 2, case_names id swap]: - \P p\ if \p permutes S\ \finite S\ - and id': \P id\ - and swap': \\a b p. a \ S \ b \ S \ p permutes S \ P p \ P (p \ transpose a b)\ -using \p permutes S\ \finite S\ proof (induction rule: permutes_induct) - case id - from id' show ?case . -next - case (swap a b p) - then have \bij p\ - using permutes_bij by blast - have \P (p \ transpose (inv p a) (inv p b))\ - by (rule swap') (auto simp add: swap permutes_in_image permutes_inv) - also have \p \ transpose (inv p a) (inv p b) = transpose a b \ p\ - using \bij p\ by (rule transpose_comp_eq [symmetric]) - finally show ?case . -qed +lemma permutes_doubleton_iff: "f permutes {a, b} \ f = id \ f = Transposition.transpose a b" +proof (cases "a = b") + case False + have "{id, Transposition.transpose a b} \ {f. f permutes {a, b}}" + by (auto simp: permutes_id permutes_swap_id) + moreover have "id \ Transposition.transpose a b" + using False by (auto simp: fun_eq_iff Transposition.transpose_def) + hence "card {id, Transposition.transpose a b} = card {f. f permutes {a, b}}" + using False by (simp add: card_permutations) + ultimately have "{id, Transposition.transpose a b} = {f. f permutes {a, b}}" + by (intro card_subset_eq finite_permutations) auto + thus ?thesis by auto +qed auto subsection \Permutations of index set for iterated operations\ @@ -922,7 +1163,7 @@ by (auto elim: permutation_permutesE intro: permutes_imp_permutation) -subsection \Sign of a permutation as a real number\ +subsection \Sign of a permutation\ definition sign :: \('a \ 'a) \ int\ \ \TODO: prefer less generic name\ where \sign p = (if evenperm p then 1 else - 1)\ @@ -957,7 +1198,444 @@ \sign p * (sign p * sign q) = sign q\ by (simp add: sign_def) -term "(bij, bij_betw, permutation)" +lemma abs_sign [simp]: "\sign p\ = 1" + by (simp add: sign_def) + + +subsection \An induction principle in terms of transpositions\ + +definition apply_transps :: "('a \ 'a) list \ 'a \ 'a" where + "apply_transps xs = foldr (\) (map (\(a,b). Transposition.transpose a b) xs) id" + +lemma apply_transps_Nil [simp]: "apply_transps [] = id" + by (simp add: apply_transps_def) + +lemma apply_transps_Cons [simp]: + "apply_transps (x # xs) = Transposition.transpose (fst x) (snd x) \ apply_transps xs" + by (simp add: apply_transps_def case_prod_unfold) + +lemma apply_transps_append [simp]: + "apply_transps (xs @ ys) = apply_transps xs \ apply_transps ys" + by (induction xs) auto + +lemma permutation_apply_transps [simp, intro]: "permutation (apply_transps xs)" +proof (induction xs) + case (Cons x xs) + thus ?case + unfolding apply_transps_Cons by (intro permutation_compose permutation_swap_id) +qed auto + +lemma permutes_apply_transps: + assumes "\(a,b)\set xs. a \ A \ b \ A" + shows "apply_transps xs permutes A" + using assms +proof (induction xs) + case (Cons x xs) + from Cons.prems show ?case + unfolding apply_transps_Cons + by (intro permutes_compose permutes_swap_id Cons) auto +qed (auto simp: permutes_id) + + +lemma permutes_induct [consumes 2, case_names id swap]: + assumes "p permutes S" "finite S" + assumes "P id" + assumes "\a b p. a \ S \ b \ S \ a \ b \ P p \ p permutes S + \ P (Transposition.transpose a b \ p)" + shows "P p" + using assms(2,1,4) +proof (induct S arbitrary: p rule: finite_induct) + case empty + then show ?case using assms by (auto simp: id_def) +next + case (insert x F p) + let ?r = "Transposition.transpose x (p x) \ p" + let ?q = "Transposition.transpose x (p x) \ ?r" + have qp: "?q = p" + by (simp add: o_assoc) + have "?r permutes F" + using permutes_insert_lemma[OF insert.prems(1)] . + have "P ?r" + by (rule insert(3)[OF \?r permutes F\], rule insert(5)) (auto intro: permutes_subset) + show ?case + proof (cases "x = p x") + case False + have "p x \ F" + using permutes_in_image[OF \p permutes _\, of x] False by auto + have "P ?q" + by (rule insert(5)) + (use \P ?r\ \p x \ F\ \?r permutes F\ False in \auto simp: o_def intro: permutes_subset\) + thus "P p" + by (simp add: qp) + qed (use \P ?r\ in simp) +qed + +lemma permutes_rev_induct[consumes 2, case_names id swap]: + assumes "finite S" "p permutes S" + assumes "P id" + assumes "\a b p. a \ S \ b \ S \ a \ b \ P p \ p permutes S + \ P (p \ Transposition.transpose a b)" + shows "P p" +proof - + have "inv_into UNIV p permutes S" + using assms by (intro permutes_inv) + from this and assms(1,2) show ?thesis + proof (induction "inv_into UNIV p" arbitrary: p rule: permutes_induct) + case id + hence "p = id" + by (metis inv_id permutes_inv_inv) + thus ?case using \P id\ by (auto simp: id_def) + next + case (swap a b p p') + have "p = Transposition.transpose a b \ (Transposition.transpose a b \ p)" + by (simp add: o_assoc) + also have "\ = Transposition.transpose a b \ inv_into UNIV p'" + by (subst swap.hyps) auto + also have "Transposition.transpose a b = inv_into UNIV (Transposition.transpose a b)" + by (simp add: inv_swap_id) + also have "\ \ inv_into UNIV p' = inv_into UNIV (p' \ Transposition.transpose a b)" + using swap \finite S\ + by (intro permutation_inverse_compose [symmetric] permutation_swap_id permutation_inverse) + (auto simp: permutation_permutes) + finally have "p = inv (p' \ Transposition.transpose a b)" . + moreover have "p' \ Transposition.transpose a b permutes S" + by (intro permutes_compose permutes_swap_id swap) + ultimately have *: "P (p' \ Transposition.transpose a b)" + by (rule swap(4)) + have "P (p' \ Transposition.transpose a b \ Transposition.transpose a b)" + by (rule assms; intro * swap permutes_compose permutes_swap_id) + also have "p' \ Transposition.transpose a b \ Transposition.transpose a b = p'" + by (simp flip: o_assoc) + finally show ?case . + qed +qed + +lemma map_permutation_apply_transps: + assumes f: "inj_on f A" and "set ts \ A \ A" + shows "map_permutation A f (apply_transps ts) = apply_transps (map (map_prod f f) ts)" + using assms(2) +proof (induction ts) + case (Cons t ts) + obtain a b where [simp]: "t = (a, b)" + by (cases t) + have "map_permutation A f (apply_transps (t # ts)) = + map_permutation A f (Transposition.transpose a b \ apply_transps ts)" + by simp + also have "\ = map_permutation A f (Transposition.transpose a b) \ + map_permutation A f (apply_transps ts)" + by (subst map_permutation_compose') + (use f Cons.prems in \auto intro!: permutes_apply_transps\) + also have "map_permutation A f (Transposition.transpose a b) = + Transposition.transpose (f a) (f b)" + by (intro map_permutation_transpose f) (use Cons.prems in auto) + also have "map_permutation A f (apply_transps ts) = apply_transps (map (map_prod f f) ts)" + by (intro Cons.IH) (use Cons.prems in auto) + also have "Transposition.transpose (f a) (f b) \ apply_transps (map (map_prod f f) ts) = + apply_transps (map (map_prod f f) (t # ts))" + by simp + finally show ?case . +qed (use f in \auto simp: map_permutation_id'\) + + +lemma permutes_from_transpositions: + assumes "p permutes A" "finite A" + shows "\xs. (\(a,b)\set xs. a \ b \ a \ A \ b \ A) \ apply_transps xs = p" + using assms +proof (induction rule: permutes_induct) + case id + thus ?case by (intro exI[of _ "[]"]) auto +next + case (swap a b p) + from swap.IH obtain xs where + xs: "(\(a,b)\set xs. a \ b \ a \ A \ b \ A)" "apply_transps xs = p" + by blast + thus ?case + using swap.hyps by (intro exI[of _ "(a,b) # xs"]) auto +qed + + +subsection \More on the sign of permutations\ + +lemma evenperm_apply_transps_iff: + assumes "\(a,b)\set xs. a \ b" + shows "evenperm (apply_transps xs) \ even (length xs)" + using assms + by (induction xs) + (simp_all add: case_prod_unfold evenperm_comp permutation_swap_id evenperm_swap) + +lemma evenperm_map_permutation: + assumes f: "inj_on f A" and "p permutes A" "finite A" + shows "evenperm (map_permutation A f p) \ evenperm p" +proof - + note [simp] = inj_on_eq_iff[OF f] + obtain ts where ts: "\(a, b)\set ts. a \ b \ a \ A \ b \ A" "apply_transps ts = p" + using permutes_from_transpositions[OF assms(2,3)] by blast + have "evenperm p \ even (length ts)" + by (subst ts(2) [symmetric], subst evenperm_apply_transps_iff) (use ts(1) in auto) + also have "\ \ even (length (map (map_prod f f) ts))" + by simp + also have "\ \ evenperm (apply_transps (map (map_prod f f) ts))" + by (subst evenperm_apply_transps_iff) (use ts(1) in auto) + also have "apply_transps (map (map_prod f f) ts) = map_permutation A f p" + unfolding ts(2)[symmetric] + by (rule map_permutation_apply_transps [symmetric]) (use f ts(1) in auto) + finally show ?thesis .. +qed + +lemma sign_map_permutation: + assumes "inj_on f A" "p permutes A" "finite A" + shows "sign (map_permutation A f p) = sign p" + unfolding sign_def by (subst evenperm_map_permutation) (use assms in auto) + + +text \ + Sometimes it can be useful to consider the sign of a function that is not a permutation in the + Isabelle/HOL sense, but its restriction to some finite subset is. +\ +definition sign_on :: "'a set \ ('a \ 'a) \ int" + where "sign_on A f = sign (restrict_id f A)" + +lemma sign_on_cong [cong]: + assumes "A = B" "\x. x \ A \ f x = g x" + shows "sign_on A f = sign_on B g" + unfolding sign_on_def using assms + by (intro arg_cong[of _ _ sign] restrict_id_cong) + +lemma sign_on_permutes: + assumes "f permutes A" "A \ B" + shows "sign_on B f = sign f" +proof - + have f: "f permutes B" + using assms permutes_subset by blast + have "sign_on B f = sign (restrict_id f B)" + by (simp add: sign_on_def) + also have "restrict_id f B = f" + using f by (auto simp: fun_eq_iff permutes_not_in restrict_id_def) + finally show ?thesis . +qed + +lemma sign_on_id [simp]: "sign_on A id = 1" + by (subst sign_on_permutes[of _ A]) auto + +lemma sign_on_ident [simp]: "sign_on A (\x. x) = 1" + using sign_on_id[of A] unfolding id_def by simp + +lemma sign_on_transpose: + assumes "a \ A" "b \ A" "a \ b" + shows "sign_on A (Transposition.transpose a b) = -1" + by (subst sign_on_permutes[of _ A]) + (use assms in \auto simp: permutes_swap_id sign_swap_id\) + +lemma sign_on_compose: + assumes "bij_betw f A A" "bij_betw g A A" "finite A" + shows "sign_on A (f \ g) = sign_on A f * sign_on A g" +proof - + define restr where "restr = (\f. restrict_id f A)" + have "sign_on A (f \ g) = sign (restr (f \ g))" + by (simp add: sign_on_def restr_def) + also have "restr (f \ g) = restr f \ restr g" + using assms(2) by (auto simp: restr_def fun_eq_iff bij_betw_def restrict_id_def) + also have "sign \ = sign (restr f) * sign (restr g)" unfolding restr_def + by (rule sign_compose) (auto intro!: permutes_imp_permutation[of A] permutes_restrict_id assms) + also have "\ = sign_on A f * sign_on A g" + by (simp add: sign_on_def restr_def) + finally show ?thesis . +qed + + + +subsection \Transpositions of adjacent elements\ + +text \ + We have shown above that every permutation can be written as a product of transpositions. + We will now furthermore show that any transposition of successive natural numbers + $\{m, \ldots, n\}$ can be written as a product of transpositions of \<^emph>\adjacent\ elements, + i.e.\ transpositions of the form $i \leftrightarrow i+1$. +\ + +function adj_transp_seq :: "nat \ nat \ nat list" where + "adj_transp_seq a b = + (if a \ b then [] + else if b = a + 1 then [a] + else a # adj_transp_seq (a+1) b @ [a])" + by auto +termination by (relation "measure (\(a,b). b - a)") auto + +lemmas [simp del] = adj_transp_seq.simps + +lemma length_adj_transp_seq: + "a < b \ length (adj_transp_seq a b) = 2 * (b - a) - 1" + by (induction a b rule: adj_transp_seq.induct; subst adj_transp_seq.simps) auto + + +definition apply_adj_transps :: "nat list \ nat \ nat" + where "apply_adj_transps xs = foldl (\) id (map (\x. Transposition.transpose x (x+1)) xs)" + +lemma apply_adj_transps_aux: + "f \ foldl (\) g (map (\x. Transposition.transpose x (Suc x)) xs) = + foldl (\) (f \ g) (map (\x. Transposition.transpose x (Suc x)) xs)" + by (induction xs arbitrary: f g) (auto simp: o_assoc) + +lemma apply_adj_transps_Nil [simp]: "apply_adj_transps [] = id" + and apply_adj_transps_Cons [simp]: + "apply_adj_transps (x # xs) = Transposition.transpose x (x+1) \ apply_adj_transps xs" + and apply_adj_transps_snoc [simp]: + "apply_adj_transps (xs @ [x]) = apply_adj_transps xs \ Transposition.transpose x (x+1)" + by (simp_all add: apply_adj_transps_def apply_adj_transps_aux) + +lemma adj_transp_seq_correct: + assumes "a < b" + shows "apply_adj_transps (adj_transp_seq a b) = Transposition.transpose a b" + using assms +proof (induction a b rule: adj_transp_seq.induct) + case (1 a b) + show ?case + proof (cases "b = a + 1") + case True + thus ?thesis + by (subst adj_transp_seq.simps) (auto simp: o_def Transposition.transpose_def apply_adj_transps_def) + next + case False + hence "apply_adj_transps (adj_transp_seq a b) = + Transposition.transpose a (Suc a) \ Transposition.transpose (Suc a) b \ Transposition.transpose a (Suc a)" + using 1 by (subst adj_transp_seq.simps) + (simp add: o_assoc swap_id_common swap_id_common' id_def o_def) + also have "\ = Transposition.transpose a b" + using False 1 by (simp add: Transposition.transpose_def fun_eq_iff) + finally show ?thesis . + qed +qed + +lemma permutation_apply_adj_transps: "permutation (apply_adj_transps xs)" +proof (induction xs) + case (Cons x xs) + have "permutation (Transposition.transpose x (Suc x) \ apply_adj_transps xs)" + by (intro permutation_compose permutation_swap_id Cons) + thus ?case by (simp add: o_def) +qed auto + +lemma permutes_apply_adj_transps: + assumes "\x\set xs. x \ A \ Suc x \ A" + shows "apply_adj_transps xs permutes A" + using assms + by (induction xs) (auto intro!: permutes_compose permutes_swap_id permutes_id) + +lemma set_adj_transp_seq: + "a < b \ set (adj_transp_seq a b) = {a..Transferring properties of permutations along bijections\ + +locale permutes_bij = + fixes p :: "'a \ 'a" and A :: "'a set" and B :: "'b set" + fixes f :: "'a \ 'b" and f' :: "'b \ 'a" + fixes p' :: "'b \ 'b" + defines "p' \ (\x. if x \ B then f (p (f' x)) else x)" + assumes permutes_p: "p permutes A" + assumes bij_f: "bij_betw f A B" + assumes f'_f: "x \ A \ f' (f x) = x" +begin + +lemma bij_f': "bij_betw f' B A" + using bij_f f'_f by (auto simp: bij_betw_def) (auto simp: inj_on_def image_image) + +lemma f_f': "x \ B \ f (f' x) = x" + using f'_f bij_f by (auto simp: bij_betw_def) + +lemma f_in_B: "x \ A \ f x \ B" + using bij_f by (auto simp: bij_betw_def) + +lemma f'_in_A: "x \ B \ f' x \ A" + using bij_f' by (auto simp: bij_betw_def) + +lemma permutes_p': "p' permutes B" +proof - + have p': "p' x = x" if "x \ B" for x + using that by (simp add: p'_def) + have bij_p: "bij_betw p A A" + using permutes_p by (simp add: permutes_imp_bij) + have "bij_betw (f \ p \ f') B B" + by (rule bij_betw_trans bij_f bij_f' bij_p)+ + also have "?this \ bij_betw p' B B" + by (intro bij_betw_cong) (auto simp: p'_def) + finally show ?thesis + using p' by (rule bij_imp_permutes) +qed + +lemma f_eq_iff [simp]: "f x = f y \ x = y" if "x \ A" "y \ A" for x y + using that bij_f by (auto simp: bij_betw_def inj_on_def) + +lemma apply_transps_map_f_aux: + assumes "\(a,b)\set xs. a \ A \ b \ A" "y \ B" + shows "apply_transps (map (map_prod f f) xs) y = f (apply_transps xs (f' y))" + using assms +proof (induction xs arbitrary: y) + case Nil + thus ?case by (auto simp: f_f') +next + case (Cons x xs y) + from Cons.prems have "apply_transps xs permutes A" + by (intro permutes_apply_transps) auto + hence [simp]: "apply_transps xs z \ A \ z \ A" for z + by (simp add: permutes_in_image) + from Cons show ?case + by (auto simp: Transposition.transpose_def f_f' f'_f case_prod_unfold f'_in_A) +qed + +lemma apply_transps_map_f: + assumes "\(a,b)\set xs. a \ A \ b \ A" + shows "apply_transps (map (map_prod f f) xs) = + (\y. if y \ B then f (apply_transps xs (f' y)) else y)" +proof + fix y + show "apply_transps (map (map_prod f f) xs) y = + (if y \ B then f (apply_transps xs (f' y)) else y)" + proof (cases "y \ B") + case True + thus ?thesis + using apply_transps_map_f_aux[OF assms] by simp + next + case False + have "apply_transps (map (map_prod f f) xs) permutes B" + using assms by (intro permutes_apply_transps) (auto simp: case_prod_unfold f_in_B) + with False have "apply_transps (map (map_prod f f) xs) y = y" + by (intro permutes_not_in) + with False show ?thesis + by simp + qed +qed + +end + + +locale permutes_bij_finite = permutes_bij + + assumes finite_A: "finite A" +begin + +lemma evenperm_p'_iff: "evenperm p' \ evenperm p" +proof - + obtain xs where xs: "\(a,b)\set xs. a \ A \ b \ A \ a \ b" "apply_transps xs = p" + using permutes_from_transpositions[OF permutes_p finite_A] by blast + have "evenperm p \ evenperm (apply_transps xs)" + using xs by simp + also have "\ \ even (length xs)" + using xs by (intro evenperm_apply_transps_iff) auto + also have "\ \ even (length (map (map_prod f f) xs))" + by simp + also have "\ \ evenperm (apply_transps (map (map_prod f f) xs))" using xs + by (intro evenperm_apply_transps_iff [symmetric]) (auto simp: case_prod_unfold) + also have "apply_transps (map (map_prod f f) xs) = p'" + using xs unfolding p'_def by (subst apply_transps_map_f) auto + finally show ?thesis .. +qed + +lemma sign_p': "sign p' = sign p" + by (auto simp: sign_def evenperm_p'_iff) + +end + subsection \Permuting a list\ diff -r 06aac7eaec29 -r 71304514891e src/HOL/Combinatorics/Transposition.thy --- a/src/HOL/Combinatorics/Transposition.thy Sun Jun 01 20:01:22 2025 +0200 +++ b/src/HOL/Combinatorics/Transposition.thy Tue Jun 03 12:22:58 2025 +0200 @@ -10,7 +10,7 @@ lemma transpose_apply_first [simp]: \transpose a b a = b\ by (simp add: transpose_def) - + lemma transpose_apply_second [simp]: \transpose a b b = a\ by (simp add: transpose_def) @@ -43,6 +43,9 @@ \transpose a b \ transpose a b = id\ by (rule ext) simp +lemma transpose_eq_id_iff: "Transposition.transpose x y = id \ x = y" + by (auto simp: fun_eq_iff Transposition.transpose_def) + lemma transpose_triple: \transpose a b (transpose b c (transpose a b d)) = transpose a c d\ if \a \ c\ and \b \ c\