Theory Linear_Maps

theory Linear_Maps
imports Rank
header{*Linear Maps*}

theory Linear_Maps
imports
Rank
begin

(*
Title: Linear_Maps.thy
Author: Jose Divasón <jose.divasonm at unirioja.es>
Author: Jesús Aransay <jesus-maria.aransay at unirioja.es>
Maintainer: Jose Divasón <jose.divasonm at unirioja.es>
*)


subsection{*Properties about ranks and linear maps*}

lemma rank_matrix_dim_range:
assumes lf: "linear f"
shows "rank (matrix f) = dim (range f)" unfolding rank_col_rank col_rank_def
unfolding col_space_eq' using matrix_works[OF lf] by metis

text{*The following two lemmas are the demonstration of theorem 2.11 that appears the book "Advanced Linear Algebra" by Steven Roman.*}

lemma linear_injective_rank_eq_ncols:
assumes lf: "linear f"
shows "inj f <-> rank (matrix f) = ncols (matrix f)"
proof (rule)
assume inj: "inj f"
hence "{x. f x = 0} = {0}" using linear_injective_ker_0[OF lf] by blast
hence "dim {x. f x = 0} = 0" using dim_zero_eq' by blast
thus "rank (matrix f) = ncols (matrix f)" using rank_nullity_theorem[OF lf] unfolding ncols_def
using rank_matrix_dim_range[OF lf] by fastforce
next
assume eq: "rank (matrix f) = ncols (matrix f)"
have "dim {x. f x = 0} = 0" using rank_nullity_theorem[OF lf] unfolding ncols_def
using rank_matrix_dim_range[OF lf] eq
by (metis DIM_cart DIM_real add_eq_self_zero monoid_mult_class.mult.right_neutral nat_add_commute ncols_def)
hence "{x. f x = 0} = {0}" using linear_0[OF lf] dim_zero_eq by auto
thus "inj f" unfolding linear_injective_ker_0[OF lf] .
qed

lemma linear_surjective_rank_eq_ncols:
assumes lf: "linear f"
shows "surj f <-> rank (matrix f) = nrows (matrix f)"
proof (rule)
assume surj: "surj f"
have "nrows (matrix f) = CARD ('b)" unfolding nrows_def ..
also have "... = dim (range f)" by (metis surj basis_exists independent_is_basis is_basis_def top_le)
also have "... = rank (matrix f)" unfolding rank_matrix_dim_range[OF lf] ..
finally show "rank (matrix f) = nrows (matrix f)" ..
next
assume "rank (matrix f) = nrows (matrix f)"
hence "dim (range f) = CARD ('b)" unfolding rank_matrix_dim_range[OF lf] nrows_def .
thus " surj f" by (metis (mono_tags) basis_exists dim_UNIV dim_subset_UNIV independent_is_basis is_basis_def lf subspace_UNIV subspace_dim_equal subspace_linear_image top_le)
qed

lemma linear_bij_rank_eq_ncols:
fixes f::"(real^'n)=>(real^'n)"
assumes lf: "linear f"
shows "bij f <-> rank (matrix f) = ncols (matrix f)"
unfolding bij_def
using linear_injective_imp_surjective[OF lf]
using linear_surjective_imp_injective[OF lf]
using linear_injective_rank_eq_ncols[OF lf]
by auto


subsection{*Invertible linear maps*}

text{*We could get rid of the property @{term "linear g"} using @{thm "left_inverse_linear"}*}

definition invertible_lf ::"('a::euclidean_space => 'a::euclidean_space) => bool"
where "invertible_lf f = (linear f ∧ (∃g. linear g ∧ (g o f = id) ∧ (f o g = id)))"

lemma invertible_lf_intro[intro]:
assumes "linear f" and "(g o f = id)" and "(f o g = id)"
shows "invertible_lf f"
by (metis assms(1) assms(2) invertible_lf_def left_inverse_linear linear_inverse_left)

lemma invertible_imp_bijective:
assumes "invertible_lf f"
shows "bij f"
by (metis assms bij_betw_comp_iff bij_betw_imp_surj invertible_lf_def inj_on_imageI2 inj_on_imp_bij_betw inv_id surj_id surj_imp_inj_inv)

lemma invertible_matrix_imp_invertible_lf:
fixes A::"real^'n^'n"
assumes invertible_A: "invertible A"
shows "invertible_lf (λx. A *v x)"
proof -
obtain B where AB: "A**B=mat 1" and BA: "B**A=mat 1" using invertible_A unfolding invertible_def by blast
show ?thesis
proof (rule invertible_lf_intro [of _ "(λx. B *v x)"])
show l: "linear (op *v A)" using matrix_vector_mul_linear .
show id1: "op *v B o op *v A = id" by (metis (lifting) AB BA isomorphism_expand matrix_vector_mul_assoc matrix_vector_mul_lid)
show "op *v A o op *v B = id" by (metis l id1 left_inverse_linear linear_inverse_left)
qed
qed

lemma invertible_lf_imp_invertible_matrix:
fixes f::"real^'n=>real^'n"
assumes invertible_f: "invertible_lf f"
shows "invertible (matrix f)"
proof -
obtain g where linear_g: "linear g" and gf: "(g o f = id)" and fg: "(f o g = id)" using invertible_f unfolding invertible_lf_def by auto
show ?thesis proof (unfold invertible_def, rule exI[of _ "matrix g"], rule conjI)
show "matrix f ** matrix g = mat 1"
by (metis (no_types) fg id_def left_inverse_linear linear_g linear_id matrix_compose matrix_eq matrix_mul_rid matrix_vector_mul matrix_vector_mul_assoc)
show "matrix g ** matrix f = mat 1"
by (metis `matrix f ** matrix g = mat 1` matrix_left_right_inverse)
qed
qed

lemma invertible_matrix_iff_invertible_lf:
fixes A::"real^'n^'n"
shows "invertible A <-> invertible_lf (λx. A *v x)"
by (metis invertible_lf_imp_invertible_matrix invertible_matrix_imp_invertible_lf matrix_of_matrix_vector_mul)

lemma invertible_matrix_iff_invertible_lf':
fixes f::"real^'n=>real^'n"
assumes linear_f: "linear f"
shows "invertible (matrix f) <-> invertible_lf f"
by (metis (lifting) assms invertible_matrix_iff_invertible_lf matrix_vector_mul)


lemma invertible_matrix_mult_right_rank:
fixes A::"real^'n^'m" and Q::"real^'n^'n"
assumes invertible_Q: "invertible Q"
shows "rank (A**Q) = rank A"
proof -
def TQ=="(λx. Q *v x)"
def TA"(λx. A *v x)"
def TAQ=="(λx. (A**Q) *v x)"
have "invertible_lf TQ" using invertible_matrix_imp_invertible_lf[OF invertible_Q] unfolding TQ_def .
hence bij_TQ: "bij TQ" using invertible_imp_bijective by auto
have "range TAQ = range (TA o TQ)" unfolding TQ_def TA_def TAQ_def o_def matrix_vector_mul_assoc ..
also have "... = TA `(range TQ)" unfolding image_compose ..
also have "... = TA ` (UNIV)" using bij_is_surj[OF bij_TQ] by simp
finally have "range TAQ = range TA" .
thus ?thesis unfolding rank_eq_dim_image TAQ_def TA_def by simp
qed



lemma subspace_image_invertible_mat:
fixes P::"real^'m^'m"
assumes inv_P: "invertible P"
and sub_W: "subspace W"
shows "subspace ((λx. P *v x)` W)"
by (metis (lifting) matrix_vector_mul_linear sub_W subspace_linear_image)


lemma dim_image_invertible_mat:
fixes P::"real^'m^'m"
assumes inv_P: "invertible P"
and sub_W: "subspace W"
shows "dim ((λx. P *v x)` W) = dim W"
proof -
obtain B where B_in_W: "B⊆W" and ind_B: "independent B" and W_in_span_B: "W ⊆ span B" and card_B_eq_dim_W: "card B = dim W"
using basis_exists by blast
def L"(λx. P *v x)"
def C"L`B"
have finite_B: "finite B" using indep_card_eq_dim_span[OF ind_B] by simp
have linear_L: "linear L" using matrix_vector_mul_linear unfolding L_def .
have finite_C: "finite C" using indep_card_eq_dim_span[OF ind_B] unfolding C_def by simp
have inv_TP: "invertible_lf (λx. P *v x)" using invertible_matrix_imp_invertible_lf[OF inv_P] .
have inj_on_LW: "inj_on L W" using invertible_imp_bijective[OF inv_TP] unfolding bij_def L_def unfolding inj_on_def
by blast
hence inj_on_LB: "inj_on L B" unfolding inj_on_def using B_in_W by auto
have ind_D: "independent C"
proof (rule independent_if_scalars_zero[OF finite_C], clarify)
fix f x
assume setsum: "(∑x∈C. f x *R x) = 0" and x: "x ∈ C"
obtain y where Ly_eq_x: "L y = x" and y: "y ∈ B" using x unfolding C_def L_def by auto
have "(∑x∈C. f x *R x) = setsum ((λx. f x *R x) o L) B" unfolding C_def by (rule setsum_reindex[OF inj_on_LB])
also have "... = setsum (λx. f (L x) *R L x) B" unfolding o_def ..
also have "... = setsum (λx. ((f o L) x) *R L x) B" using o_def by auto
also have "... = L (setsum (λx. ((f o L) x) *R x) B)" by (rule linear_setsum_mul[OF linear_L finite_B,symmetric])
finally have rw: " (∑x∈C. f x *R x) = L (∑x∈B. (f o L) x *R x)" .
have "(∑x∈B. (f o L) x *R x) ∈ W" by (rule subspace_setsum[OF sub_W finite_B], auto simp add: B_in_W set_rev_mp sub_W subspace_mul)
hence "(∑x∈B. (f o L) x *R x)=0" using setsum rw using linear_injective_on_subspace_0[OF linear_L sub_W] using inj_on_LW by auto
hence "(f o L) y = 0" using scalars_zero_if_independent[OF finite_B ind_B, of "(f o L)"] using y by auto
thus "f x = 0" unfolding o_def Ly_eq_x .
qed
have "L` W ⊆ span C"
proof (unfold span_finite[OF finite_C], clarify)
fix xa assume xa_in_W: "xa ∈ W"
obtain g where setsum_g: "setsum (λx. g x *R x) B = xa" using span_finite[OF finite_B] W_in_span_B xa_in_W by blast
show "∃u. (∑v∈C. u v *R v) = L xa"
proof (rule exI[of _ "λx. g (THE y. y ∈ B ∧ x=L y)"])
have "L xa = L (setsum (λx. g x *R x) B)" using setsum_g by simp
also have "... = setsum (λx. g x *R L x) B" using linear_setsum_mul[OF linear_L finite_B] .
also have "... = setsum (λx. g (THE y. y ∈ B ∧ x=L y) *R x) (L`B)"
proof (unfold setsum_reindex[OF inj_on_LB], unfold o_def, rule setsum_cong2)
fix x assume x_in_B: "x∈B"
have x_eq_the:"x = (THE y. y ∈ B ∧ L x = L y)"
proof (rule the_equality[symmetric])
show "x ∈ B ∧ L x = L x" using x_in_B by auto
show "!!y. y ∈ B ∧ L x = L y ==> y = x" using inj_on_LB x_in_B unfolding inj_on_def by fast
qed
show "g x *R L x = g (THE y. y ∈ B ∧ L x = L y) *R L x" using x_eq_the by simp
qed
finally show "(∑v∈C. g (THE y. y ∈ B ∧ v = L y) *R v) = L xa" unfolding C_def ..
qed
qed
have "card C = card B" using card_image[OF inj_on_LB] unfolding C_def .
thus ?thesis
by (metis Convex_Euclidean_Space.span_eq L_def dim_image_eq inj_on_LW linear_L sub_W)
qed


lemma invertible_matrix_mult_left_rank:
fixes A::"real^'n^'m" and P::"real^'m^'m"
assumes invertible_P: "invertible P"
shows "rank (P**A) = rank A"
proof -
def TP=="(λx. P *v x)"
def TA"(λx. A *v x)"
def TPA=="(λx. (P**A) *v x)"
have sub: "subspace (range (op *v A))" by (metis matrix_vector_mul_linear subspace_UNIV subspace_linear_image)
have "dim (range TPA) = dim (range (TP o TA))" unfolding TP_def TA_def TPA_def o_def matrix_vector_mul_assoc ..
also have "... = dim (range TA)" using dim_image_invertible_mat[OF invertible_P sub] unfolding TP_def TA_def o_def image_compose[symmetric] .
finally show ?thesis unfolding rank_eq_dim_image TPA_def TA_def .
qed

corollary invertible_matrices_mult_rank:
fixes A::"real^'n^'m" and P::"real^'m^'m" and Q::"real^'n^'n"
assumes invertible_P: "invertible P"
and invertible_Q: "invertible Q"
shows "rank (P**A**Q) = rank A"
using invertible_matrix_mult_right_rank[OF invertible_Q] using invertible_matrix_mult_left_rank[OF invertible_P] by metis


lemma invertible_matrix_mult_left_rank':
fixes A::"real^'n^'m" and P::"real^'m^'m"
assumes invertible_P: "invertible P" and B_eq_PA: "B=P**A"
shows "rank B = rank A"
proof -
have "rank B = rank (P**A)" using B_eq_PA by auto
also have "... = rank A" using invertible_matrix_mult_left_rank[OF invertible_P] by auto
finally show ?thesis .
qed

lemma invertible_matrix_mult_right_rank':
fixes A::"real^'n^'m" and Q::"real^'n^'n"
assumes invertible_Q: "invertible Q" and B_eq_PA: "B=A**Q"
shows "rank B = rank A" by (metis B_eq_PA invertible_Q invertible_matrix_mult_right_rank)

lemma invertible_matrices_rank':
fixes A::"real^'n^'m" and P::"real^'m^'m" and Q::"real^'n^'n"
assumes invertible_P: "invertible P" and invertible_Q: "invertible Q" and B_eq_PA: "B = P**A**Q"
shows "rank B = rank A" by (metis B_eq_PA invertible_P invertible_Q invertible_matrices_mult_rank)

subsection{*Definition and properties of the set of a vector*}

text{*Some definitions:*}

definition set_of_vector :: "'a^'n => 'a set"
where "set_of_vector A = {A $ i |i. i ∈ UNIV}"

definition cart_basis' :: " real^'n^'n"
where "cart_basis' = (χ i. axis i 1)"

lemma basis_image_linear:
assumes invertible_lf: "invertible_lf f"
and basis_X: "is_basis (set_of_vector X)"
shows "is_basis (f` (set_of_vector X))"
proof (rule iffD1[OF independent_is_basis], rule conjI)
have "card (f ` set_of_vector X) = card (set_of_vector X)"
by (rule card_image[of f "set_of_vector X"], metis invertible_imp_bijective[OF invertible_lf] bij_def inj_eq inj_on_def)
also have "... = card (UNIV::'a set)" using independent_is_basis basis_X by auto
finally show "card (f ` set_of_vector X) = card (UNIV::'a set)" .
show "independent (f ` set_of_vector X)"
proof (rule independent_injective_image)
show "independent (set_of_vector X)" using basis_X unfolding is_basis_def by simp
show "linear f" using invertible_lf unfolding invertible_lf_def by simp
show "inj f" using invertible_imp_bijective[OF invertible_lf] unfolding bij_def by simp
qed
qed

text{*Properties about @{thm "cart_basis'_def"}*}

lemma set_of_vector_cart_basis':
shows "(set_of_vector cart_basis') = {axis i 1 :: real^'n | i. i ∈ (UNIV :: 'n set)}"
unfolding set_of_vector_def cart_basis'_def by auto

lemma cart_basis'_i: "cart_basis' $ i = axis i 1" unfolding cart_basis'_def by simp

lemma finite_cart_basis':
shows "finite (set_of_vector cart_basis')"
unfolding set_of_vector_def using finite_Atleast_Atmost_nat[of "λi. (cart_basis'::real^'a^'a) $ i"] .

lemma axis_Basis:"{axis i (1::real) |i. i ∈ (UNIV::('a::finite set))} = Basis"
proof (auto)
fix i::"'n::finite" show "axis i (1::real) ∈ Basis" proof (rule axis_in_Basis)
show "(1::real) ∈ Basis" using Basis_real_def by simp
qed
next
fix x::"real^'n" assume x: "x ∈ Basis" show "∃i. x = axis i 1" using x unfolding Basis_vec_def by auto
qed

lemma span_stdbasis:"span {axis i 1 :: real^'n | i. i ∈ (UNIV :: 'n set)} = UNIV"
unfolding span_Basis[symmetric] unfolding axis_Basis by auto

lemma independent_stdbasis: "independent {axis i 1 ::real^'n |i. i∈ (UNIV :: 'n set)}"
by (rule independent_substdbasis, auto, rule axis_in_Basis, auto)

lemma span_cart_basis':
shows "span (set_of_vector cart_basis') = UNIV"
unfolding set_of_vector_def unfolding cart_basis'_def using span_stdbasis by auto

lemma is_basis_cart_basis': "is_basis (set_of_vector (cart_basis'))"
by (metis (lifting) independent_stdbasis is_basis_def set_of_vector_cart_basis' span_stdbasis)

lemma basis_expansion_cart_basis':"setsum (λi. x$i *R cart_basis' $ i) UNIV = x" unfolding cart_basis'_def using basis_expansion apply auto
proof -
assume ass:"(!!x::(real, 'a) vec. (∑i∈UNIV. x $ i *s axis i 1) = x)"
have "(∑i∈UNIV. x $ i *s axis i 1) = x" using ass[of x] .
thus "(∑i∈UNIV. x $ i *R axis i 1) = x" unfolding scalar_mult_eq_scaleR .
qed

lemma basis_expansion_unique:
"setsum (λi. f i *s axis (i::'n::finite) 1) UNIV = (x::('a::comm_ring_1) ^'n) <-> (∀i. f i = x$i)"
proof (auto simp add: basis_expansion)
fix i::"'n"
have univ_rw: "UNIV = (UNIV - {i}) ∪ {i}" by fastforce
have "(∑x∈UNIV. f x * axis x 1 $ i) = setsum (λx. f x * axis x 1 $ i) (UNIV - {i} ∪ {i})" using univ_rw by simp
also have "... = setsum (λx. f x * axis x 1 $ i) (UNIV - {i}) + setsum (λx. f x * axis x 1 $ i) {i}" by (rule setsum_Un_disjoint, auto)
also have "... = f i" unfolding axis_def by auto
finally show "f i = (∑x∈UNIV. f x * axis x 1 $ i)" ..
qed

lemma basis_expansion_cart_basis'_unique: "setsum (λi. f (cart_basis' $ i) *R cart_basis' $ i) UNIV = x <-> (∀i. f (cart_basis' $ i) = x$i)"
using basis_expansion_unique unfolding cart_basis'_def
by (simp add: vec_eq_iff setsum_delta if_distrib cong del: if_weak_cong)

lemma basis_expansion_cart_basis'_unique': "setsum (λi. f i *R cart_basis' $ i) UNIV = x <-> (∀i. f i = x$i)"
using basis_expansion_unique unfolding cart_basis'_def
by (simp add: vec_eq_iff setsum_delta if_distrib cong del: if_weak_cong)

text{*Properties of @{thm "is_basis_def"}.*}

lemma setsum_basis_eq:
fixes X::"real^'n^'n"
assumes is_basis:"is_basis (set_of_vector X)"
shows "setsum (λx. f x *R x) (set_of_vector X) = setsum (λi. f (X$i) *R (X$i)) UNIV"
proof (rule setsum_reindex_cong[of "λi. X$i"])
show fact_1: "set_of_vector X = range (op $ X)" unfolding set_of_vector_def by auto
have card_set_of_vector:"card(set_of_vector X) = CARD('n)" using independent_is_basis[of "set_of_vector X"] using is_basis by auto
show "inj (op $ X)"
proof (rule eq_card_imp_inj_on)
show "finite (UNIV::'n set)" using finite_class.finite_UNIV .
show "card (range (op $ X)) = card (UNIV::'n set)" using card_set_of_vector using fact_1 unfolding set_of_vector_def by simp
qed
show "!!a. a ∈ UNIV ==> f (X $ a) *R X $ a = f (X $ a) *R X $ a" by simp
qed

corollary setsum_basis_eq2:
fixes X::"real^'n^'n"
assumes is_basis:"is_basis (set_of_vector X)"
shows "setsum (λx. f x *R x) (set_of_vector X) = setsum (λi. (f o op $ X) i *R (X$i)) UNIV" using setsum_basis_eq[OF is_basis] by simp

lemma inj_op_nth:
fixes X::"real^'n^'n"
assumes is_basis: "is_basis (set_of_vector X)"
shows "inj (op $ X)"
proof -
have fact_1: "set_of_vector X = range (op $ X)" unfolding set_of_vector_def by auto
have card_set_of_vector:"card(set_of_vector X) = CARD('n)" using independent_is_basis[of "set_of_vector X"] using is_basis by auto
show "inj (op $ X)"
proof (rule eq_card_imp_inj_on)
show "finite (UNIV::'n set)" using finite_class.finite_UNIV .
show "card (range (op $ X)) = card (UNIV::'n set)" using card_set_of_vector using fact_1 unfolding set_of_vector_def by simp
qed
qed

lemma basis_UNIV:
fixes X::"real^'n^'n"
assumes is_basis: "is_basis (set_of_vector X)"
shows "UNIV = {x. ∃g. (∑i∈UNIV. g i *R X$i) = x}"
proof -
have "UNIV = {x. ∃g. (∑i∈(set_of_vector X). g i *R i) = x}" using is_basis unfolding is_basis_def using span_finite[OF basis_finite[OF is_basis]] by simp
also have "... ⊆ {x. ∃g. (∑i∈UNIV. g i *R X$i) = x}"
proof (clarify)
fix f
show "∃g. (∑i∈UNIV. g i *R X $ i) = (∑i∈set_of_vector X. f i *R i)"
proof (rule exI[of _ "(λi. (f o op $ X) i)"], unfold o_def, rule setsum_reindex_cong[symmetric, of "op $ X"])
show fact_1: "set_of_vector X = range (op $ X)" unfolding set_of_vector_def by auto
have card_set_of_vector:"card(set_of_vector X) = CARD('n)" using independent_is_basis[of "set_of_vector X"] using is_basis by auto
show "inj (op $ X)" using inj_op_nth[OF is_basis] .
show "!!a. a ∈ UNIV ==> f (X $ a) *R X $ a = f (X $ a) *R X $ a" by simp
qed
qed
finally show ?thesis by auto
qed

lemma scalars_zero_if_basis:
fixes X::"real^'n^'n"
assumes is_basis: "is_basis (set_of_vector X)" and setsum: "(∑i∈(UNIV::'n set). f i *R X$i) = 0"
shows "∀i∈(UNIV::'n set). f i = 0"
proof -
have ind_X: "independent (set_of_vector X)" using is_basis unfolding is_basis_def by simp
have finite_X:"finite (set_of_vector X)" using basis_finite[OF is_basis] .
have 1: "(∀g. (∑v∈(set_of_vector X). g v *R v) = 0 --> (∀v∈(set_of_vector X). g v = 0))" using ind_X unfolding independent_explicit using finite_X by auto
def g"λv. f (THE i. X $ i = v)"
have "(∑v∈(set_of_vector X). g v *R v) = 0"
proof -
have "(∑v∈(set_of_vector X). g v *R v) = (∑i∈(UNIV::'n set). f i *R X$i)"
proof (rule setsum_reindex_cong)
show "inj (op $ X)" using inj_op_nth[OF is_basis] .
show "set_of_vector X = range (op $ X)" unfolding set_of_vector_def by auto
show "!!a. a ∈ (UNIV::'n set) ==> f a *R X $ a = g (X $ a) *R X $ a"
proof (auto)
fix a
assume "X $ a ≠ 0"
show "f a = g (X $ a)"
unfolding g_def using inj_op_nth[OF is_basis]
by (metis (lifting, mono_tags) injD the_equality)
qed
qed
thus ?thesis unfolding setsum .
qed
hence 2: "∀v∈(set_of_vector X). g v = 0" using 1 by auto
show ?thesis
proof (clarify)
fix a
have "g (X$a) = 0" using 2 unfolding set_of_vector_def by auto
thus "f a = 0" unfolding g_def using inj_op_nth[OF is_basis]
by (metis (lifting, mono_tags) injD the_equality)
qed
qed

lemma basis_combination_unique:
fixes X::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)" and setsum_eq: "(∑i∈UNIV. g i *R X$i) = (∑i∈UNIV. f i *R X$i)"
shows "f=g"
proof (rule ccontr)
assume "f≠g"
from this obtain x where fx_gx: "f x ≠ g x" by fast
have "0=(∑i∈UNIV. g i *R X$i) - (∑i∈UNIV. f i *R X$i)" using setsum_eq by simp
also have "... = (∑i∈UNIV. g i *R X$i - f i *R X$i)" unfolding setsum_subtractf[symmetric] ..
also have "... = (∑i∈UNIV. (g i - f i) *R X$i)" by (rule setsum_cong2, simp add: scaleR_diff_left)
also have "... = (∑i∈UNIV. (g - f) i *R X$i)" by simp
finally have setsum_eq_1: "0 = (∑i∈UNIV. (g - f) i *R X$i)" by simp
have " ∀i∈UNIV. (g - f) i = 0" by (rule scalars_zero_if_basis[OF basis_X setsum_eq_1[symmetric]])
hence "(g - f) x = 0" by simp
hence "f x = g x" by simp
thus False using fx_gx by contradiction
qed


subsection{*Coordinates of a vector*}

text{*Definition and properties of the coordinates of a vector (in terms of a particular ordered basis).*}

definition coord :: "real^'n^'n=>real^'n=>real^'n"
where "coord X v = (χ i. (THE f. v = setsum (λx. f x *R X$x) UNIV) i)"

text{*@{term "coord X v"} are the coordinates of vector @{term "v"} with respect to the basis @{term "X"}*}

lemma bij_coord:
fixes X::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)"
shows "bij (coord X)"
proof (unfold bij_def, auto)
show inj: "inj (coord X)"
proof (unfold inj_on_def, auto)
fix x y assume coord_eq: "coord X x = coord X y"
obtain f where f: "(∑x∈UNIV. f x *R X $ x) = x" using basis_UNIV[OF basis_X] by blast
obtain g where g: "(∑x∈UNIV. g x *R X $ x) = y" using basis_UNIV[OF basis_X] by blast
have the_f: "(THE f. x = (∑x∈UNIV. f x *R X $ x)) = f"
proof (rule the_equality)
show "x = (∑x∈UNIV. f x *R X $ x)" using f by simp
show "!!fa. x = (∑x∈UNIV. fa x *R X $ x) ==> fa = f" using basis_combination_unique[OF basis_X] f by simp
qed
have the_g: "(THE g. y = (∑x∈UNIV. g x *R X $ x)) = g"
proof (rule the_equality)
show "y = (∑x∈UNIV. g x *R X $ x)" using g by simp
show "!!ga. y = (∑x∈UNIV. ga x *R X $ x) ==> ga = g" using basis_combination_unique[OF basis_X] g by simp
qed
have "(THE f. x = (∑x∈UNIV. f x *R X $ x)) = (THE g. y = (∑x∈UNIV. g x *R X $ x))"
using coord_eq unfolding coord_def
using vec_lambda_inject[of "(THE f. x = (∑x∈UNIV. f x *R X $ x)) " "(THE f. y = (∑x∈UNIV. f x *R X $ x))"]
by auto
hence "f = g" unfolding the_f the_g .
thus "x=y" using f g by simp
qed
next
fix x::"(real, 'n) vec"
show "x ∈ range (coord X)"
proof (unfold image_def, auto, rule exI[of _ "setsum (λi. x$i *R X$i) UNIV"], unfold coord_def)
def f"λi. x$i"
have the_f: " (THE f. (∑i∈UNIV. x $ i *R X $ i) = (∑x∈UNIV. f x *R X $ x)) = f"
proof (rule the_equality)
show "(∑i∈UNIV. x $ i *R X $ i) = (∑x∈UNIV. f x *R X $ x)" unfolding f_def ..
fix g assume setsum_eq:"(∑i∈UNIV. x $ i *R X $ i) = (∑x∈UNIV. g x *R X $ x)"
show "g = f" using basis_combination_unique[OF basis_X] using setsum_eq unfolding f_def by simp
qed
show " x = vec_lambda (THE f. (∑i∈UNIV. x $ i *R X $ i) = (∑x∈UNIV. f x *R X $ x))" unfolding the_f unfolding f_def using vec_lambda_eta[of x] by simp
qed
qed


lemma linear_coord:
fixes X::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)"
shows "linear (coord X)"
proof (unfold linear_def additive_def linear_axioms_def coord_def, auto)
fix x y::"(real, 'n) vec"
show "vec_lambda (THE f. x + y = (∑x∈UNIV. f x *R X $ x)) = vec_lambda (THE f. x = (∑x∈UNIV. f x *R X $ x)) + vec_lambda (THE f. y = (∑x∈UNIV. f x *R X $ x))"
proof -
obtain f where f: "(∑a∈(UNIV::'n set). f a *R X $ a) = x + y" using basis_UNIV[OF basis_X] by blast
obtain g where g: " (∑x∈UNIV. g x *R X $ x) = x" using basis_UNIV[OF basis_X] by blast
obtain h where h: "(∑x∈UNIV. h x *R X $ x) = y" using basis_UNIV[OF basis_X] by blast
def t"λi. g i + h i"
have the_f: "(THE f. x + y = (∑x∈UNIV. f x *R X $ x)) = f"
proof (rule the_equality)
show " x + y = (∑x∈UNIV. f x *R X $ x)" using f by simp
show "!!fa. x + y = (∑x∈UNIV. fa x *R X $ x) ==> fa = f" using basis_combination_unique[OF basis_X] f by simp
qed
have the_g: "(THE g. x = (∑x∈UNIV. g x *R X $ x)) = g"
proof (rule the_equality)
show "x = (∑x∈UNIV. g x *R X $ x)" using g by simp
show "!!ga. x = (∑x∈UNIV. ga x *R X $ x) ==> ga = g" using basis_combination_unique[OF basis_X] g by simp
qed
have the_h: "(THE h. y = (∑x∈UNIV. h x *R X $ x)) = h"
proof (rule the_equality)
show "y = (∑x∈UNIV. h x *R X $ x)" using h ..
show "!!ha. y = (∑x∈UNIV. ha x *R X $ x) ==> ha = h" using basis_combination_unique[OF basis_X] h by simp
qed
have "(∑a∈(UNIV::'n set). f a *R X $ a) = (∑x∈UNIV. g x *R X $ x) + (∑x∈UNIV. h x *R X $ x)" using f g h by simp
also have "... = (∑x∈UNIV. g x *R X $ x + h x *R X $ x)" unfolding setsum_addf[symmetric] ..
also have "... = (∑x∈UNIV. (g x + h x) *R X $ x)" by (rule setsum_cong2, simp add: scaleR_left_distrib)
also have "... = (∑x∈UNIV. t x *R X $ x)" unfolding t_def ..
finally have "(∑a∈UNIV. f a *R X $ a) = (∑x∈UNIV. t x *R X $ x)" .
hence "f=t" using basis_combination_unique[OF basis_X] by auto
thus ?thesis
by (unfold the_f the_g the_h, vector, auto, unfold f g h t_def, simp)
qed
next
fix c x
show " vec_lambda (THE f. c *R x = (∑x∈UNIV. f x *R X $ x)) = c *R vec_lambda (THE f. x = (∑x∈UNIV. f x *R X $ x))"
proof -
obtain f where f: "(∑x∈UNIV. f x *R X $ x) = c *R x" using basis_UNIV[OF basis_X] by blast
obtain g where g: "(∑x∈UNIV. g x *R X $ x) = x" using basis_UNIV[OF basis_X] by blast
def t"λi. c *R g i"
have the_f: "(THE f. c *R x = (∑x∈UNIV. f x *R X $ x)) = f"
proof (rule the_equality)
show " c *R x = (∑x∈UNIV. f x *R X $ x)" using f ..
show "!!fa. c *R x = (∑x∈UNIV. fa x *R X $ x) ==> fa = f" using basis_combination_unique[OF basis_X] f by simp
qed
have the_g: "(THE g. x = (∑x∈UNIV. g x *R X $ x)) = g"proof (rule the_equality)
show " x = (∑x∈UNIV. g x *R X $ x)" using g ..
show "!!ga. x = (∑x∈UNIV. ga x *R X $ x) ==> ga = g" using basis_combination_unique[OF basis_X] g by simp
qed
have "(∑x∈UNIV. f x *R X $ x) = c *R (∑x∈UNIV. g x *R X $ x)" using f g by simp
also have "... = (∑x∈UNIV. c *R g x *R X $ x)" by (rule scaleR_setsum_right)
also have "... = (∑x∈UNIV. t x *R X $ x)" unfolding t_def by simp
finally have " (∑x∈UNIV. f x *R X $ x) = (∑x∈UNIV. t x *R X $ x)" .
hence "f=t" using basis_combination_unique[OF basis_X] by auto
thus ?thesis
by (unfold the_f the_g, vector, auto, unfold t_def, auto)
qed
qed


lemma coord_eq:
assumes basis_X:"is_basis (set_of_vector X)"
and coord_eq: "coord X v = coord X w"
shows "v = w"
proof -
have "∀i. (THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)) i = (THE f. ∀i. w $ i = (∑x∈UNIV. f x * X $ x $ i)) i" using coord_eq
unfolding coord_eq coord_def vec_eq_iff by simp
hence the_eq: "(THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)) = (THE f. ∀i. w $ i = (∑x∈UNIV. f x * X $ x $ i))" by auto
obtain f where f: "(∑x∈UNIV. f x *R X $ x)= v" using basis_UNIV[OF basis_X] by blast
obtain g where g: "(∑x∈UNIV. g x *R X $ x)= w" using basis_UNIV[OF basis_X] by blast
have the_f: "(THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)) = f"
proof (rule the_equality)
show " ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)" using f by auto
fix fa assume "∀i. v $ i = (∑x∈UNIV. fa x * X $ x $ i)"
hence "∀i. v $ i = (∑x∈UNIV. fa x *R X $ x) $ i" unfolding setsum_component by simp
hence fa:" v = (∑x∈UNIV. fa x *R X $ x)" unfolding vec_eq_iff .
show "fa = f" using basis_combination_unique[OF basis_X] f fa by simp
qed
have the_g: "(THE g. ∀i. w $ i = (∑x∈UNIV. g x * X $ x $ i)) = g"
proof (rule the_equality)
show " ∀i. w $ i = (∑x∈UNIV. g x * X $ x $ i)" using g by auto
fix fa assume "∀i. w $ i = (∑x∈UNIV. fa x * X $ x $ i)"
hence "∀i. w $ i = (∑x∈UNIV. fa x *R X $ x) $ i" unfolding setsum_component by simp
hence fa:" w = (∑x∈UNIV. fa x *R X $ x)" unfolding vec_eq_iff .
show "fa = g" using basis_combination_unique[OF basis_X] g fa by simp
qed
have "f=g" using the_eq unfolding the_f the_g .
thus "v=w" using f g by blast
qed


subsection{*Matrix of change of basis and coordinate matrix of a linear map*}

text{*Definitions of matrix of change of basis and matrix of a linear transformation with respect to two bases:*}

definition matrix_change_of_basis :: "real^'n^'n =>real^'n^'n=>real^'n^'n"
where "matrix_change_of_basis X Y = (χ i j. (coord Y (X$j)) $ i)"

text{*There exists in the library the definition @{thm "matrix_def"}, which is the coordinate matrix of a linear map with respect to the standard bases.
Now we generalise that concept to the coordinate matrix of a linear map with respect to any two bases.*}


definition matrix' :: "real^'n^'n => real^'m^'m => (real^'n => real^'m) => real^'n^'m"
where "matrix' X Y f= (χ i j. (coord Y (f(X$j))) $ i)"

text{*Properties of @{thm "matrix'_def"}*}

lemma matrix'_eq_matrix:
defines cart_basis_Rn: "cart_basis_Rn == (cart_basis')::real^'n^'n" and cart_basis_Rm:"cart_basis_Rm == (cart_basis')::real^'m^'m"
assumes lf: "linear f"
shows "matrix' (cart_basis_Rn) (cart_basis_Rm) f = matrix f"
proof (unfold matrix_def matrix'_def coord_def, vector, auto)
fix i j
have basis_Rn:"is_basis (set_of_vector cart_basis_Rn)" using is_basis_cart_basis' unfolding cart_basis_Rn .
have basis_Rm:"is_basis (set_of_vector cart_basis_Rm)" using is_basis_cart_basis' unfolding cart_basis_Rm .
obtain g where setsum_g: "(∑x∈UNIV. g x *R (cart_basis_Rm $ x)) = f (cart_basis_Rn $ j)" using basis_UNIV[OF basis_Rm] by blast
have the_g: "(THE g. ∀a. f (cart_basis_Rn $ j) $ a = (∑x∈UNIV. g x * cart_basis_Rm $ x $ a)) = g"
proof (rule the_equality, clarify)
fix a
have "f (cart_basis_Rn $ j) $ a = (∑i∈UNIV. g i *R (cart_basis_Rm $ i)) $ a" using setsum_g by simp
also have "... = (∑x∈UNIV. g x * cart_basis_Rm $ x $ a)" unfolding setsum_component by simp
finally show "f (cart_basis_Rn $ j) $ a = (∑x∈UNIV. g x * cart_basis_Rm $ x $ a)" .
fix ga assume "∀a. f (cart_basis_Rn $ j) $ a = (∑x∈UNIV. ga x * cart_basis_Rm $ x $ a)"
hence setsum_ga: "f (cart_basis_Rn $ j) = (∑i∈UNIV. ga i *R cart_basis_Rm $ i)" by (vector, auto)
show "ga = g"
proof (rule basis_combination_unique)
show "is_basis (set_of_vector (cart_basis_Rm))" using basis_Rm .
show " (∑i∈UNIV. g i *R cart_basis_Rm $ i) = (∑i∈UNIV. ga i *R cart_basis_Rm $ i)" using setsum_g setsum_ga by simp
qed
qed
show " (THE fa. ∀i. f (cart_basis_Rn $ j) $ i = (∑x∈UNIV. fa x * cart_basis_Rm $ x $ i)) i = f (axis j 1) $ i"
unfolding the_g using setsum_g unfolding cart_basis_Rm cart_basis_Rn cart_basis'_def using basis_expansion_unique[of g "f (axis j 1)"]
unfolding scalar_mult_eq_scaleR by auto
qed

lemma matrix':
assumes linear_f: "linear f" and basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows "f (X$i) = setsum (λj. (matrix' X Y f) $ j $ i *R (Y$j)) UNIV"
proof (unfold matrix'_def coord_def matrix_mult_vsum column_def, vector, auto)
fix j
obtain g where g: "(∑x∈UNIV. g x *R Y $ x) = f (X $ i)" using basis_UNIV[OF basis_Y] by blast
have the_g: "(THE fa. ∀ia. f (X $ i) $ ia = (∑x∈UNIV. fa x * Y $ x $ ia)) = g"
proof (rule the_equality, clarify)
fix a
have "f (X $ i) $ a = (∑x∈UNIV. g x *R Y $ x) $ a" using g by simp
also have "... = (∑x∈UNIV. g x * Y $ x $ a)" unfolding setsum_component by auto
finally show "f (X $ i) $ a = (∑x∈UNIV. g x * Y $ x $ a)" .
fix fa
assume "∀ia. f (X $ i) $ ia = (∑x∈UNIV. fa x * Y $ x $ ia)"
hence " ∀ia. f (X $ i) $ ia = (∑x∈UNIV. fa x *R Y $ x) $ ia" unfolding setsum_component by simp
hence fa:"f (X $ i) = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff .
show "fa = g" by (rule basis_combination_unique[OF basis_Y], simp add: fa g)
qed
show " f (X $ i) $ j = (∑x∈UNIV. (THE fa. ∀j. f (X $ i) $ j = (∑x∈UNIV. fa x * Y $ x $ j)) x * Y $ x $ j)"
unfolding the_g unfolding g[symmetric] setsum_component by simp
qed


corollary matrix'2:
assumes linear_f: "linear f" and basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
and eq_f: "∀i. f (X$i) = setsum (λj. A $ j $ i *R (Y$j)) UNIV"
shows "matrix' X Y f = A"
proof -
have eq_f': "∀i. f (X$i) = setsum (λj. (matrix' X Y f) $ j $ i *R (Y$j)) UNIV" using matrix'[OF linear_f basis_X basis_Y] by auto
show ?thesis
proof (vector, auto)
fix i j
def a"λx. (matrix' X Y f) $ x $ i"
def b"λx. A $ x $ i"
have fxi_1:"f (X$i) = setsum (λj. a j *R (Y$j)) UNIV" using eq_f' unfolding a_def by simp
have fxi_2: "f (X$i) = setsum (λj. b j *R (Y$j)) UNIV" using eq_f unfolding b_def by simp
have "a=b" using basis_combination_unique[OF basis_Y] fxi_1 fxi_2 by auto
thus "(matrix' X Y f) $ j $ i = A $ j $ i" unfolding a_def b_def by metis
qed
qed

text{*This is the theorem 2.14 in the book "Advanced Linear Algebra" by Steven Roman.*}
lemma coord_matrix':
fixes X::"real^'n^'n" and Y::"real^'m^'m"
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)" and linear_f: "linear f"
shows "coord Y (f v) = (matrix' X Y f) *v (coord X v)"
proof (unfold matrix_mult_vsum matrix'_def column_def coord_def, vector, auto)
fix i
obtain g where g: "(∑x∈UNIV. g x *R Y $ x) = f v" using basis_UNIV[OF basis_Y] by auto
obtain s where s: "(∑x∈UNIV. s x *R X $ x) = v" using basis_UNIV[OF basis_X] by auto
have the_g: "(THE fa. ∀a. f v $ a = (∑x∈UNIV. fa x * Y $ x $ a)) = g"
proof (rule the_equality)
have "∀a. f v $ a = (∑x∈UNIV. g x *R Y $ x) $ a" using g by simp
thus " ∀a. f v $ a = (∑x∈UNIV. g x * Y $ x $ a)" unfolding setsum_component by simp
fix fa assume " ∀a. f v $ a = (∑x∈UNIV. fa x * Y $ x $ a)"
hence fa: "f v = (∑x∈UNIV. fa x *R Y $ x)" by (vector, auto)
show "fa=g" by (rule basis_combination_unique[OF basis_Y], simp add: fa g)
qed
have the_s: "(THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i))=s"
proof (rule the_equality)
have " ∀i. v $ i = (∑x∈UNIV. s x *R X $ x) $ i" using s by simp
thus " ∀i. v $ i = (∑x∈UNIV. s x * X $ x $ i)"unfolding setsum_component by simp
fix fa assume "∀i. v $ i = (∑x∈UNIV. fa x * X $ x $ i)"
hence fa: "v=(∑x∈UNIV. fa x *R X $ x)" by (vector, auto)
show "fa=s"by (rule basis_combination_unique[OF basis_X], simp add: fa s)
qed
def t"λx. (∑i∈UNIV. (s i * (THE fa. f (X $ i) = (∑x∈UNIV. fa x *R Y $ x)) x))"
have "(∑x∈UNIV. g x *R Y $ x) = f v" using g by simp
also have "... = f (∑x∈UNIV. s x *R X $ x)" using s by simp
also have "... = (∑x∈UNIV. s x *R f (X $ x))" by (rule linear_setsum_mul[OF linear_f], simp)
also have "... = (∑i∈UNIV. s i *R setsum (λj. (matrix' X Y f)$j$i *R (Y$j)) UNIV)" using matrix'[OF linear_f basis_X basis_Y] by auto
also have "... = (∑i∈UNIV. ∑x∈UNIV. s i *R matrix' X Y f $ x $ i *R Y $ x)" unfolding scaleR_setsum_right ..
also have "... = (∑i∈UNIV. ∑x∈UNIV. (s i * (THE fa. f (X $ i) = (∑x∈UNIV. fa x *R Y $ x)) x) *R Y $ x)" unfolding matrix'_def unfolding coord_def by auto
also have "... = (∑x∈UNIV. (∑i∈UNIV. (s i * (THE fa. f (X $ i) = (∑x∈UNIV. fa x *R Y $ x)) x) *R Y $ x))" by (rule setsum_commute)
also have "... = (∑x∈UNIV. (∑i∈UNIV. (s i * (THE fa. f (X $ i) = (∑x∈UNIV. fa x *R Y $ x)) x)) *R Y $ x)" unfolding scaleR_setsum_left ..
also have "... = (∑x∈UNIV. t x *R Y $ x)" unfolding t_def ..
finally have "(∑x∈UNIV. g x *R Y $ x) = (∑x∈UNIV. t x *R Y $ x)" .
hence "g=t" using basis_combination_unique[OF basis_Y] by simp
thus "(THE fa. ∀i. f v $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i =
(∑x∈UNIV. (THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)) x * (THE fa. ∀i. f (X $ x) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i)"

proof (unfold the_g the_s t_def, auto)
have " (∑x∈UNIV. s x * (THE fa. ∀i. f (X $ x) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i) =
(∑x∈UNIV. s x * (THE fa. ∀i. f (X $ x) $ i = (∑x∈UNIV. fa x *R Y $ x) $ i) i)"
unfolding setsum_component by simp
also have "... = (∑x∈UNIV. s x * (THE fa. f (X $ x) = (∑x∈UNIV. fa x *R Y $ x)) i)" by (rule setsum_cong2, simp add: vec_eq_iff)
finally show " (∑ia∈UNIV. s ia * (THE fa. f (X $ ia) = (∑x∈UNIV. fa x *R Y $ x)) i) = (∑x∈UNIV. s x * (THE fa. ∀i. f (X $ x) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i)"
by auto
qed
qed

text{*This is the second part of the theorem 2.15 in the book "Advanced Linear Algebra" by Steven Roman.*}
lemma matrix'_compose:
fixes X::"real^'n^'n" and Y::"real^'m^'m" and Z::"real^'p^'p"
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)" and basis_Z: "is_basis (set_of_vector Z)"
and linear_f: "linear f" and linear_g: "linear g"
shows "matrix' X Z (g o f) = (matrix' Y Z g) ** (matrix' X Y f)"
proof (unfold matrix_eq, clarify)
fix a::"(real, 'n) vec"
obtain v where v: "a = coord X v" using bij_coord[OF basis_X] unfolding bij_iff by metis
have linear_gf: "linear (g o f)" using linear_compose[OF linear_f linear_g] .
have "matrix' X Z (g o f) *v a = matrix' X Z (g o f) *v (coord X v)" unfolding v ..
also have "... = coord Z ((g o f) v)" unfolding coord_matrix'[OF basis_X basis_Z linear_gf, symmetric] ..
also have "... = coord Z (g (f v))" unfolding o_def ..
also have "... = (matrix' Y Z g) *v (coord Y (f v))" unfolding coord_matrix'[OF basis_Y basis_Z linear_g] ..
also have "... = (matrix' Y Z g) *v ((matrix' X Y f) *v (coord X v))" unfolding coord_matrix'[OF basis_X basis_Y linear_f] ..
also have "... = ((matrix' Y Z g) ** (matrix' X Y f)) *v (coord X v)" unfolding matrix_vector_mul_assoc ..
finally show "matrix' X Z (g o f) *v a = matrix' Y Z g ** matrix' X Y f *v a" unfolding v .
qed


lemma exists_linear_eq_matrix':
fixes A::"real^'m^'n" and X::"real^'m^'m" and Y::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows "∃f. matrix' X Y f = A ∧ linear f"
proof -
def f == "λv. setsum (λj. A $ j $ (THE k. v = X $ k) *R Y $ j) UNIV"
obtain g where linear_g: "linear g" and f_eq_g: "(∀x ∈ (set_of_vector X). g x = f x)" using linear_independent_extend using basis_X unfolding is_basis_def by blast
show ?thesis
proof (rule exI[of _ g], rule conjI)
show "matrix' X Y g = A"
proof (rule matrix'2)
show "linear g" using linear_g .
show "is_basis (set_of_vector X)" using basis_X .
show "is_basis (set_of_vector Y)" using basis_Y .
show "∀i. g (X $ i) = (∑j∈UNIV. A $ j $ i *R Y $ j)"
proof (clarify)
fix i
have the_k_eq_i: "(THE k. X $ i = X $ k) = i"
proof (rule the_equality)
show "X $ i = X $ i" ..
fix k assume Xi_Xk: "X $ i = X $ k" show "k = i" using Xi_Xk basis_X inj_eq inj_op_nth by metis
qed
have Xi_in_X:"X$i ∈ (set_of_vector X)" unfolding set_of_vector_def by auto
have "g (X$i) = f (X$i)" using f_eq_g Xi_in_X by simp
also have "... = (∑j∈UNIV. A $ j $ (THE k. X $ i = X $ k) *R Y $ j)" unfolding f_def ..
also have "... = (∑j∈UNIV. A $ j $ i *R Y $ j)" unfolding the_k_eq_i ..
finally show "g (X $ i) = (∑j∈UNIV. A $ j $ i *R Y $ j)" .
qed
qed
show "linear g" using linear_g .
qed
qed



lemma linear_matrix':
assumes basis_Y: "is_basis (set_of_vector Y)"
shows "linear (matrix' X Y)"
proof (unfold linear_def additive_def linear_axioms_def, auto)
fix f g
show "matrix' X Y (f + g) = matrix' X Y f + matrix' X Y g"
proof (unfold matrix'_def coord_def, vector, auto)
fix i j
obtain a where a:"(∑x∈UNIV. a x *R Y $ x) = f (X $ j)" using basis_UNIV[OF basis_Y] by blast
obtain b where b:"(∑x∈UNIV. b x *R Y $ x) = g (X $ j)" using basis_UNIV[OF basis_Y] by blast
obtain c where c: "(∑x∈UNIV. c x *R Y $ x) = (f + g) (X $ j)" using basis_UNIV[OF basis_Y] by blast
def d"λi. a i + b i"
have "(∑x∈UNIV. c x *R Y $ x) = (f + g) (X $ j)" using c by simp
also have "... = f (X $ j) + g (X $ j)" unfolding plus_fun_def ..
also have "... = (∑x∈UNIV. a x *R Y $ x) + (∑x∈UNIV. b x *R Y $ x)" unfolding a b ..
also have "... = (∑x∈UNIV. (a x *R Y $ x) + b x *R Y $ x)" unfolding setsum_addf ..
also have "... = (∑x∈UNIV. (a x + b x) *R Y $ x)" unfolding scaleR_add_left ..
also have "... = (∑x∈UNIV. (d x) *R Y $ x)" unfolding d_def by simp
finally have "(∑x∈UNIV. c x *R Y $ x) = (∑x∈UNIV. d x *R Y $ x)" .
hence c_eq_d: "c=d" using basis_combination_unique[OF basis_Y] by simp
have the_a: "(THE fa. ∀i. f (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) = a"
proof (rule the_equality)
have " ∀i. f (X $ j) $ i = (∑x∈UNIV. a x *R Y $ x) $ i" using a unfolding vec_eq_iff by simp
thus " ∀i. f (X $ j) $ i = (∑x∈UNIV. a x * Y $ x $ i)" unfolding setsum_component by simp
fix fa assume "∀i. f (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)"
hence "f (X $ j) = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff setsum_component by simp
thus "fa = a" using basis_combination_unique[OF basis_Y] a by simp
qed
have the_b:"(THE f. ∀i. g (X $ j) $ i = (∑x∈UNIV. f x * Y $ x $ i)) = b"
proof (rule the_equality)
have "∀i. g (X $ j) $ i = (∑x∈UNIV. b x *R Y $ x) $ i" using b unfolding vec_eq_iff by simp
thus " ∀i. g (X $ j) $ i = (∑x∈UNIV. b x * Y $ x $ i)" unfolding setsum_component by simp
fix fa assume "∀i. g (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)"
hence "g (X $ j) = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff setsum_component by simp
thus "fa = b" using basis_combination_unique[OF basis_Y] b by simp
qed
have the_c: "(THE fa. ∀i. (f + g) (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) = c"
proof (rule the_equality)
have "∀i. (f + g) (X $ j) $ i = (∑x∈UNIV. c x *R Y $ x) $ i" using c unfolding vec_eq_iff by simp
thus " ∀i. (f + g) (X $ j) $ i = (∑x∈UNIV. c x * Y $ x $ i)" unfolding setsum_component by simp
fix fa assume "∀i. (f + g) (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)"
hence "(f + g) (X $ j) = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff setsum_component by simp
thus "fa = c" using basis_combination_unique[OF basis_Y] c by simp
qed
show "(THE fa. ∀i. (f + g) (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i =
(THE fa. ∀i. f (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i + (THE f. ∀i. g (X $ j) $ i = (∑x∈UNIV. f x * Y $ x $ i)) i"

unfolding the_a the_b the_c unfolding a b c using c_eq_d unfolding d_def by fast
qed
fix c
show "matrix' X Y (c *R f) = c *R matrix' X Y f"
proof (unfold matrix'_def coord_def, vector, auto)
fix i j
obtain a where a:"(∑x∈UNIV. a x *R Y $ x) = f (X $ j)" using basis_UNIV[OF basis_Y] by blast
obtain b where b: "(∑x∈UNIV. b x *R Y $ x) = (c *R f) (X $ j)" using basis_UNIV[OF basis_Y] by blast
def d"λi. c *R a i"
have the_a: "(THE fa. ∀i. f (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) = a"
proof (rule the_equality)
have "∀i. f (X $ j) $ i = (∑x∈UNIV. a x *R Y $ x) $ i" using a unfolding vec_eq_iff by simp
thus "∀i. f (X $ j) $ i = (∑x∈UNIV. a x * Y $ x $ i)" unfolding setsum_component by simp
fix fa assume "∀i. f (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)"
hence "f (X $ j) = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff setsum_component by simp
thus "fa = a" using basis_combination_unique[OF basis_Y] a by simp
qed
have the_b: "(THE fa. ∀i. (c *R f) (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) = b"
proof (rule the_equality)
have "∀i. (c *R f) (X $ j) $ i = (∑x∈UNIV. b x *R Y $ x) $ i" using b unfolding vec_eq_iff by simp
thus "∀i. (c *R f) (X $ j) $ i = (∑x∈UNIV. b x * Y $ x $ i)" unfolding setsum_component by simp
fix fa assume "∀i. (c *R f) (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)"
hence "(c *R f) (X $ j) = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff setsum_component by simp
thus "fa = b" using basis_combination_unique[OF basis_Y] b by simp
qed
have "(∑x∈UNIV. b x *R Y $ x) = (c *R f) (X $ j)" using b .
also have "... = c *R f (X $ j)" unfolding scaleR_fun_def ..
also have "... = c *R (∑x∈UNIV. a x *R Y $ x)" unfolding a ..
also have "... = (∑x∈UNIV. c *R (a x *R Y $ x))" unfolding scaleR_setsum_right ..
also have "... = (∑x∈UNIV. (c *R a x) *R Y $ x)" by auto
also have "... = (∑x∈UNIV. d x *R Y $ x)" unfolding d_def ..
finally have "(∑x∈UNIV. b x *R Y $ x) = (∑x∈UNIV. d x *R Y $ x)" .
hence "b=d" using basis_combination_unique[OF basis_Y] b by simp
thus "(THE fa. ∀i. (c *R f) (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i = c * (THE fa. ∀i. f (X $ j) $ i = (∑x∈UNIV. fa x * Y $ x $ i)) i"
unfolding the_a the_b d_def
by simp
qed
qed


lemma matrix'_surj:
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows "surj (matrix' X Y)"
proof (unfold surj_def, clarify)
fix A
show "∃f. A = matrix' X Y f"
using exists_linear_eq_matrix'[OF basis_X basis_Y, of A] unfolding matrix'_def by auto
qed


text{*Properties of @{thm "matrix_change_of_basis_def"}.*}

text{*This is the first part of the theorem 2.12 in the book "Advanced Linear Algebra" by Steven Roman.*}

lemma matrix_change_of_basis_works:
fixes X::"real^'n^'n" and Y::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)"
and basis_Y: "is_basis (set_of_vector Y)"
shows "(matrix_change_of_basis X Y) *v (coord X v) = (coord Y v)"
proof (unfold matrix_mult_vsum matrix_change_of_basis_def column_def coord_def, vector, auto)
fix i
obtain f where f: "(∑x∈UNIV. f x *R Y $ x) = v" using basis_UNIV[OF basis_Y] by blast
obtain g where g: "(∑x∈UNIV. g x *R X $ x) = v" using basis_UNIV[OF basis_X] by blast
def t"λx. (THE f. X $ x= (∑a∈UNIV. f a *R Y $ a))"
def w"λi. (∑x∈UNIV. g x *R t x i)"
have the_f:"(THE f. ∀i. v $ i = (∑x∈UNIV. f x * Y $ x $ i)) = f"
proof (rule the_equality)
show "∀i. v $ i = (∑x∈UNIV. f x * Y $ x $ i)" using f by auto
fix fa assume "∀i. v $ i = (∑x∈UNIV. fa x * Y $ x $ i)"
hence "∀i. v $ i = (∑x∈UNIV. fa x *R Y $ x) $ i" unfolding setsum_component by simp
hence fa: " v = (∑x∈UNIV. fa x *R Y $ x)" unfolding vec_eq_iff .
show "fa = f"
using basis_combination_unique[OF basis_Y] fa f by simp
qed
have the_g: "(THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)) = g"
proof (rule the_equality)
show "∀i. v $ i = (∑x∈UNIV. g x * X $ x $ i)" using g by auto
fix fa assume "∀i. v $ i = (∑x∈UNIV. fa x * X $ x $ i)"
hence "∀i. v $ i = (∑x∈UNIV. fa x *R X $ x) $ i" unfolding setsum_component by simp
hence fa: " v = (∑x∈UNIV. fa x *R X $ x)" unfolding vec_eq_iff .
show "fa = g"
using basis_combination_unique[OF basis_X] fa g by simp
qed
have "(∑x∈UNIV. f x *R Y $ x) = (∑x∈UNIV. g x *R X $ x)" unfolding f g ..
also have "... = (∑x∈UNIV. g x *R (setsum (λi. (t x i) *R Y $ i) UNIV))" unfolding t_def
proof (rule setsum_cong2)
fix x
obtain h where h: "(∑a∈UNIV. h a *R Y $ a) = X$x" using basis_UNIV[OF basis_Y] by blast
have the_h: "(THE f. X $ x = (∑a∈UNIV. f a *R Y $ a))=h"
proof (rule the_equality)
show " X $ x = (∑a∈UNIV. h a *R Y $ a)" using h by simp
fix f assume f: "X $ x = (∑a∈UNIV. f a *R Y $ a)"
show "f = h" using basis_combination_unique[OF basis_Y] f h by simp
qed
show " g x *R X $ x = g x *R (∑i∈UNIV. (THE f. X $ x = (∑a∈UNIV. f a *R Y $ a)) i *R Y $ i)" unfolding the_h h ..
qed
also have "... = (∑x∈UNIV. (setsum (λi. g x *R (t x i) *R Y $ i) UNIV))" unfolding scaleR_setsum_right ..
also have "... = (∑i∈UNIV. ∑x∈UNIV. g x *R t x i *R Y $ i)" by (rule setsum_commute)
also have "... = (∑i∈UNIV. (∑x∈UNIV. g x *R t x i) *R Y $ i)" unfolding scaleR_setsum_left by auto
finally have "(∑x∈UNIV. f x *R Y $ x) = (∑i∈UNIV. (∑x∈UNIV. g x *R t x i) *R Y $ i) " .
hence "f=w" using basis_combination_unique[OF basis_Y] unfolding w_def by auto
thus "(∑x∈UNIV. (THE f. ∀i. v $ i = (∑x∈UNIV. f x * X $ x $ i)) x * (THE f. ∀i. X $ x $ i = (∑x∈UNIV. f x * Y $ x $ i)) i) =
(THE f. ∀i. v $ i = (∑x∈UNIV. f x * Y $ x $ i)) i"
unfolding the_f the_g unfolding w_def t_def unfolding vec_eq_iff by auto
qed



lemma matrix_change_of_basis_mat_1:
fixes X::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)"
shows "matrix_change_of_basis X X = mat 1"
proof (unfold matrix_change_of_basis_def coord_def mat_def, vector, auto)
fix j::"'n"
def f"λi. if i=j then 1::real else 0"
have UNIV_rw: "UNIV = insert j (UNIV-{j})" by auto
have "(∑x∈UNIV. f x *R X $ x) = (∑x∈(insert j (UNIV-{j})). f x *R X $ x)" using UNIV_rw by simp
also have "... = (λx. f x *R X $ x) j + (∑x∈(UNIV-{j}). f x *R X $ x)" by (rule setsum_insert, simp+)
also have "... = X$j + (∑x∈(UNIV-{j}). f x *R X $ x)" unfolding f_def by simp
also have "... = X$j + 0" unfolding add_left_cancel f_def by (rule setsum_0', simp)
finally have f: "(∑x∈UNIV. f x *R X $ x) = X$j" by simp
have the_f: "(THE f. ∀i. X $ j $ i = (∑x∈UNIV. f x * X $ x $ i)) = f"
proof (rule the_equality)
show "∀i. X $ j $ i = (∑x∈UNIV. f x * X $ x $ i)" using f unfolding vec_eq_iff unfolding setsum_component by simp
fix fa assume "∀i. X $ j $ i = (∑x∈UNIV. fa x * X $ x $ i)"
hence "∀i. X $ j $ i = (∑x∈UNIV. fa x *R X $ x) $ i" unfolding setsum_component by simp
hence fa: "X $ j = (∑x∈UNIV. fa x *R X $ x)" unfolding vec_eq_iff .
show "fa = f" using basis_combination_unique[OF basis_X] fa f by simp
qed
show "(THE f. ∀i. X $ j $ i = (∑x∈UNIV. f x * X $ x $ i)) j = 1" unfolding the_f f_def by simp
fix i assume i_not_j: "i ≠ j"
show "(THE f. ∀i. X $ j $ i = (∑x∈UNIV. f x * X $ x $ i)) i = 0" unfolding the_f f_def using i_not_j by simp
qed


text{*Relationships between @{thm "matrix'_def"} and @{thm "matrix_change_of_basis_def"}.
This is the theorem 2.16 in the book "Advanced Linear Algebra" by Steven Roman.*}


lemma matrix'_matrix_change_of_basis:
fixes B::"real^'n^'n" and B'::"real^'n^'n" and C::"real^'m^'m" and C'::"real^'m^'m"
assumes basis_B: "is_basis (set_of_vector B)" and basis_B': "is_basis (set_of_vector B')"
and basis_C: "is_basis (set_of_vector C)" and basis_C': "is_basis (set_of_vector C')"
and linear_f: "linear f"
shows "matrix' B' C' f = matrix_change_of_basis C C' ** matrix' B C f ** matrix_change_of_basis B' B"
proof (unfold matrix_eq, clarify)
fix x
obtain v where v: "x=coord B' v" using bij_coord[OF basis_B'] unfolding bij_iff by metis
have "matrix_change_of_basis C C' ** matrix' B C f** matrix_change_of_basis B' B *v (coord B' v)
= matrix_change_of_basis C C' ** matrix' B C f *v (matrix_change_of_basis B' B *v (coord B' v)) "
unfolding matrix_vector_mul_assoc ..
also have "... = matrix_change_of_basis C C' ** matrix' B C f *v (coord B v)" unfolding matrix_change_of_basis_works[OF basis_B' basis_B] ..
also have "... = matrix_change_of_basis C C' *v (matrix' B C f *v (coord B v))" unfolding matrix_vector_mul_assoc ..
also have "... = matrix_change_of_basis C C' *v (coord C (f v))" unfolding coord_matrix'[OF basis_B basis_C linear_f] ..
also have "... = coord C' (f v)" unfolding matrix_change_of_basis_works[OF basis_C basis_C'] ..
also have "... = matrix' B' C' f *v coord B' v" unfolding coord_matrix'[OF basis_B' basis_C' linear_f] ..
finally show " matrix' B' C' f *v x = matrix_change_of_basis C C' ** matrix' B C f ** matrix_change_of_basis B' B *v x" unfolding v ..
qed

lemma matrix'_id_eq_matrix_change_of_basis:
fixes X::"real^'n^'n" and Y::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows "matrix' X Y (id) = matrix_change_of_basis X Y"
unfolding matrix'_def matrix_change_of_basis_def unfolding id_def ..


text{*Relationships among @{thm "invertible_lf_def"}, @{thm "matrix_change_of_basis_def"}, @{thm "matrix'_def"} and @{thm "invertible_def"}.*}

text{*This is the second part of the theorem 2.12 in the book "Advanced Linear Algebra" by Steven Roman.*}

lemma matrix_inv_matrix_change_of_basis:
fixes X::"real^'n^'n" and Y::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows"matrix_change_of_basis Y X = matrix_inv (matrix_change_of_basis X Y)"
proof (rule matrix_inv_unique[symmetric])
have linear_id: "linear id" by (metis linear_id)
have "(matrix_change_of_basis Y X) ** (matrix_change_of_basis X Y) = (matrix' Y X id) ** (matrix' X Y id)"
unfolding matrix'_id_eq_matrix_change_of_basis[OF basis_X basis_Y]
unfolding matrix'_id_eq_matrix_change_of_basis[OF basis_Y basis_X] ..
also have "... = matrix' X X (id o id)" using matrix'_compose[OF basis_X basis_Y basis_X linear_id linear_id] ..
also have "... = matrix_change_of_basis X X" using matrix'_id_eq_matrix_change_of_basis[OF basis_X basis_X] unfolding o_def id_def .
also have "... = mat 1" using matrix_change_of_basis_mat_1[OF basis_X] .
finally show "matrix_change_of_basis Y X ** matrix_change_of_basis X Y = mat 1" .
have "(matrix_change_of_basis X Y) ** (matrix_change_of_basis Y X) = (matrix' X Y id) ** (matrix' Y X id)"
unfolding matrix'_id_eq_matrix_change_of_basis[OF basis_X basis_Y]
unfolding matrix'_id_eq_matrix_change_of_basis[OF basis_Y basis_X] ..
also have "... = matrix' Y Y (id o id)" using matrix'_compose[OF basis_Y basis_X basis_Y linear_id linear_id] ..
also have "... = matrix_change_of_basis Y Y" using matrix'_id_eq_matrix_change_of_basis[OF basis_Y basis_Y] unfolding o_def id_def .
also have "... = mat 1" using matrix_change_of_basis_mat_1[OF basis_Y] .
finally show "matrix_change_of_basis X Y ** matrix_change_of_basis Y X = mat 1" .
qed

text{*The following four lemmas are the proof of the theorem 2.13 in the book "Advanced Linear Algebra" by Steven Roman.*}

corollary invertible_matrix_change_of_basis:
fixes X::"real^'n^'n" and Y::"real^'n^'n"
assumes basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows "invertible (matrix_change_of_basis X Y)"
by (metis basis_X basis_Y invertible_left_inverse linear_id matrix'_id_eq_matrix_change_of_basis matrix'_matrix_change_of_basis matrix_change_of_basis_mat_1)


lemma invertible_lf_imp_invertible_matrix':
assumes "invertible_lf f" and basis_X: "is_basis (set_of_vector X)" and basis_Y: "is_basis (set_of_vector Y)"
shows "invertible (matrix' X Y f)"
by (metis (lifting) assms(1) basis_X basis_Y invertible_lf_def invertible_lf_imp_invertible_matrix
invertible_matrix_change_of_basis invertible_mult is_basis_cart_basis' matrix'_eq_matrix matrix'_matrix_change_of_basis)


lemma invertible_matrix'_imp_invertible_lf:
assumes "invertible (matrix' X Y f)" and basis_X: "is_basis (set_of_vector X)"
and linear_f: "linear f" and basis_Y: "is_basis (set_of_vector Y)"
shows "invertible_lf f"
by (metis assms(1) basis_X basis_Y id_o invertible_matrix_change_of_basis
invertible_matrix_iff_invertible_lf' invertible_mult is_basis_cart_basis' linear_f linear_id
matrix'_compose matrix'_eq_matrix matrix'_id_eq_matrix_change_of_basis o_id)


lemma invertible_matrix_is_change_of_basis:
assumes invertible_P: "invertible P" and basis_X: "is_basis (set_of_vector X)"
shows "∃!Y. matrix_change_of_basis Y X = P ∧ is_basis (set_of_vector Y)"
proof (auto)
show "∃Y. matrix_change_of_basis Y X = P ∧ is_basis (set_of_vector Y)"
proof -
fix i j
obtain f where P: "P = matrix' X X f" and linear_f: "linear f" using exists_linear_eq_matrix'[OF basis_X basis_X, of P] by blast
show ?thesis
proof (rule exI[of _ "χ j. f (X$j)"], rule conjI)
show "matrix_change_of_basis (χ j. f (X $ j)) X = P" unfolding matrix_change_of_basis_def P matrix'_def by vector
have invertible_f: "invertible_lf f" using invertible_matrix'_imp_invertible_lf[OF _ basis_X linear_f basis_X] using invertible_P unfolding P by simp
have rw: "set_of_vector (χ j. f (X $ j)) = f`(set_of_vector X)" unfolding set_of_vector_def by auto
show "is_basis (set_of_vector (χ j. f (X $ j)))" unfolding rw using basis_image_linear[OF invertible_f basis_X] .
qed
qed
fix Y Z
assume basis_Y:"is_basis (set_of_vector Y)" and eq: "matrix_change_of_basis Z X = matrix_change_of_basis Y X" and basis_Z: "is_basis (set_of_vector Z)"
have ZY_coord: "∀i. coord X (Z$i) = coord X (Y$i)" using eq unfolding matrix_change_of_basis_def unfolding vec_eq_iff by vector
show "Y=Z" by (vector, metis ZY_coord coord_eq[OF basis_X])
qed


subsection{*Equivalent Matrices*}

text{*Next definition follows the one presented in Modern Algebra by Seth Warner.*}

definition "equivalent_matrices A B = (∃P Q. invertible P ∧ invertible Q ∧ B = (matrix_inv P)**A**Q)"

lemma exists_basis: "∃X::real^'n^'n. is_basis (set_of_vector X)" using is_basis_cart_basis' by auto

lemma equivalent_implies_exist_matrix':
assumes equivalent: "equivalent_matrices A B"
shows "∃X Y X' Y' f::real^'n=>real^'m.
linear f ∧ matrix' X Y f = A ∧ matrix' X' Y' f = B ∧ is_basis (set_of_vector X) ∧ is_basis (set_of_vector Y) ∧ is_basis (set_of_vector X') ∧ is_basis (set_of_vector Y')"

proof -
obtain X::"real^'n^'n" where X: "is_basis (set_of_vector X)" using exists_basis by blast
obtain Y::"real^'m^'m" where Y: "is_basis (set_of_vector Y)" using exists_basis by blast
obtain P Q where B_PAQ: "B=(matrix_inv P)**A**Q" and inv_P: "invertible P" and inv_Q: "invertible Q" using equivalent unfolding equivalent_matrices_def by auto
obtain f where f_A: "matrix' X Y f = A" and linear_f: "linear f" using exists_linear_eq_matrix'[OF X Y] by auto
obtain X'::"real^'n^'n" where X': "is_basis (set_of_vector X')" and Q:"matrix_change_of_basis X' X = Q" using invertible_matrix_is_change_of_basis[OF inv_Q X] by fast
obtain Y'::"real^'m^'m" where Y': "is_basis (set_of_vector Y')" and P: "matrix_change_of_basis Y' Y = P" using invertible_matrix_is_change_of_basis[OF inv_P Y] by fast
have matrix_inv_P: "matrix_change_of_basis Y Y' = matrix_inv P" using matrix_inv_matrix_change_of_basis[OF Y' Y] P by simp
have "matrix' X' Y' f = matrix_change_of_basis Y Y' ** matrix' X Y f ** matrix_change_of_basis X' X" using matrix'_matrix_change_of_basis[OF X X' Y Y' linear_f] .
also have "... = (matrix_inv P) ** A ** Q" unfolding matrix_inv_P f_A Q ..
also have "... = B" using B_PAQ ..
finally show ?thesis using f_A X X' Y Y' linear_f by fast
qed


lemma exist_matrix'_implies_equivalent:
assumes A: "matrix' X Y f = A"
and B: "matrix' X' Y' f = B"
and X: "is_basis (set_of_vector X)"
and Y: "is_basis (set_of_vector Y)"
and X': "is_basis (set_of_vector X')"
and Y': "is_basis (set_of_vector Y')"
and linear_f: "linear f"
shows "equivalent_matrices A B"
proof (unfold equivalent_matrices_def, rule exI[of _ "matrix_change_of_basis Y' Y"], rule exI[of _ "matrix_change_of_basis X' X"], auto)
have inv: "matrix_change_of_basis Y Y' = matrix_inv (matrix_change_of_basis Y' Y)" using matrix_inv_matrix_change_of_basis[OF Y' Y] .
show "invertible (matrix_change_of_basis Y' Y)" using invertible_matrix_change_of_basis[OF Y' Y] .
show "invertible (matrix_change_of_basis X' X)" using invertible_matrix_change_of_basis[OF X' X] .
have "B = matrix' X' Y' f" using B ..
also have "... = matrix_change_of_basis Y Y' ** matrix' X Y f ** matrix_change_of_basis X' X" using matrix'_matrix_change_of_basis[OF X X' Y Y' linear_f] .
finally show "B = matrix_inv (matrix_change_of_basis Y' Y) ** A ** matrix_change_of_basis X' X" unfolding inv unfolding A .
qed

text{*This is the proof of the theorem 2.18 in the book "Advanced Linear Algebra" by Steven Roman.*}
corollary equivalent_iff_exist_matrix':
shows "equivalent_matrices A B <-> (∃X Y X' Y' f::real^'n=>real^'m.
linear f ∧ matrix' X Y f = A ∧ matrix' X' Y' f = B ∧ is_basis (set_of_vector X) ∧ is_basis (set_of_vector Y) ∧ is_basis (set_of_vector X') ∧ is_basis (set_of_vector Y'))"

by (rule, auto simp add: exist_matrix'_implies_equivalent equivalent_implies_exist_matrix')


subsection{*Similar matrices*}

definition similar_matrices :: "'a::{semiring_1}^'n^'n => 'a::{semiring_1}^'n^'n => bool"
where "similar_matrices A B = (∃P. invertible P ∧ B=(matrix_inv P)**A**P)"

lemma similar_implies_exist_matrix':
fixes A B::"real^'n^'n"
assumes similar: "similar_matrices A B"
shows "∃X Y f. linear f ∧ matrix' X X f = A ∧ matrix' Y Y f = B ∧ is_basis (set_of_vector X) ∧ is_basis (set_of_vector Y)"
proof -
obtain P where inv_P: "invertible P" and B_PAP: "B=(matrix_inv P)**A**P" using similar unfolding similar_matrices_def by blast
obtain X::"real^'n^'n" where X: "is_basis (set_of_vector X)" using exists_basis by blast
obtain f where linear_f: "linear f" and A: "matrix' X X f = A" using exists_linear_eq_matrix'[OF X X] by blast
obtain Y::"real^'n^'n" where Y: "is_basis (set_of_vector Y)" and P: "P = matrix_change_of_basis Y X" using invertible_matrix_is_change_of_basis[OF inv_P X] by fast
have P': "matrix_inv P = matrix_change_of_basis X Y" by (metis (lifting) P X Y matrix_inv_matrix_change_of_basis)
have "B = (matrix_inv P)**A**P" using B_PAP .
also have "... = matrix_change_of_basis X Y ** matrix' X X f ** P " unfolding P' A ..
also have "... = matrix_change_of_basis X Y ** matrix' X X f ** matrix_change_of_basis Y X" unfolding P ..
also have "... = matrix' Y Y f" using matrix'_matrix_change_of_basis[OF X Y X Y linear_f] by simp
finally show ?thesis using X Y A linear_f by fast
qed


lemma exist_matrix'_implies_similar:
fixes A B::"real^'n^'n"
assumes linear_f: "linear f" and A: "matrix' X X f = A" and B: "matrix' Y Y f = B" and X: "is_basis (set_of_vector X)" and Y: "is_basis (set_of_vector Y)"
shows "similar_matrices A B"
proof (unfold similar_matrices_def, rule exI[of _ "matrix_change_of_basis Y X"], rule conjI)
have "B=matrix' Y Y f" using B ..
also have "... = matrix_change_of_basis X Y ** matrix' X X f ** matrix_change_of_basis Y X" using matrix'_matrix_change_of_basis[OF X Y X Y linear_f] by simp
also have "... = matrix_inv (matrix_change_of_basis Y X) ** A ** matrix_change_of_basis Y X" unfolding A matrix_inv_matrix_change_of_basis[OF Y X] ..
finally show "B = matrix_inv (matrix_change_of_basis Y X) ** A ** matrix_change_of_basis Y X" .
show "invertible (matrix_change_of_basis Y X)" using invertible_matrix_change_of_basis[OF Y X] .
qed

text{*This is the proof of the theorem 2.19 in the book "Advanced Linear Algebra" by Steven Roman.*}
corollary similar_iff_exist_matrix':
fixes A B::"real^'n^'n"
shows "similar_matrices A B <-> (∃X Y f. linear f ∧ matrix' X X f = A ∧ matrix' Y Y f = B ∧ is_basis (set_of_vector X) ∧ is_basis (set_of_vector Y))"
by (rule, auto simp add: exist_matrix'_implies_similar similar_implies_exist_matrix')

end