Theory Misc_Matrix_Results

theory Misc_Matrix_Results
  imports "Commuting_Hermitian.Commuting_Hermitian"
          "BenOr_Kozen_Reif.More_Matrix"
          "Jordan_Normal_Form.Spectral_Radius"
          "Jordan_Normal_Form.DL_Rank_Submatrix"
          "Jordan_Normal_Form.Jordan_Normal_Form_Uniqueness"
          "Jordan_Normal_Form.VS_Connect"
          "QHLProver.Complex_Matrix"
          "Fishers_Inequality.Matrix_Vector_Extras"
          "Complex_Bounded_Operators.Extra_Jordan_Normal_Form"
          "Hermite_Lindemann.Misc_HLW"
begin

(* For some reason, importing Commuting_Hermitian includes a bunch of legacy stuff,
   which forces us to qualify any references to things from the Jordan_Normal_Form theory.
   The following hide_ commands suppress these legacy things so that we don't need
   to qualify things from Jordan_Normal_Form.
*)
hide_type (open)
  Finite_Cartesian_Product.vec
  Matrix_Legacy.vec
  Matrix_Legacy.mat

hide_fact (open)
  Matrix_Legacy.mat_def
  Finite_Cartesian_Product.mat_def
  Finite_Cartesian_Product.row_def
  Matrix_Legacy.row_def
  Matrix_Legacy.row_def
  Matrix_Legacy.col_def
  Determinants.det_def
  Determinants.det_def
  Finite_Cartesian_Product.vec_def
  Matrix_Legacy.vec_def
  Linear_Algebra.adjoint_def

hide_const (open)
  Matrix_Legacy.mat
  Finite_Cartesian_Product.mat
  Finite_Cartesian_Product.row
  Matrix_Legacy.row
  Matrix_Legacy.row
  Matrix_Legacy.col
  Determinants.det
  Determinants.det
  Finite_Cartesian_Product.vec
  Matrix_Legacy.vec
  Linear_Algebra.adjoint

no_notation fps_nth (infixl "$" 75)

unbundle no inner_syntax
unbundle no vec_syntax

(* There are two definitions of the spectrum of a matrix: one in Spectrual_Radius and 
   one in Projective_Measurements. We prove that they are equal. We choose to hide the
   Spectral_Radius definition as its qualified name is shorter than the alternative. *)
hide_const (open) Spectral_Radius.spectrum
hide_fact (open) Spectral_Radius.spectrum_def

section "Polynomial Factorization"

lemma idom_poly_factor_unique_aux:
  fixes rs rs' :: "'a::idom list"
  assumes eq: "(∏a←rs. [:a, 1:]) = (∏a←rs'. [:a, 1:])"
  shows "mset rs ⊆# mset rs'"
  using eq                        
  by (metis count_list_eq_length_filter count_mset poly_linear_exp_linear_factors
      poly_linear_exp_linear_factors_rev subseteq_mset_def)

lemma idom_poly_factor_unique:
  fixes rs rs' :: "'a::idom list"
  assumes eq: "(∏a←rs. [:a, 1:]) = (∏a←rs'. [:a, 1:])"
  shows "mset rs = mset rs'"
  using idom_poly_factor_unique_aux[OF eq] idom_poly_factor_unique_aux[OF eq[symmetric]]
  by order

lemma idom_poly_factor_unique':
  fixes rs rs' :: "'a::idom list"
  assumes eq: "(∏a←rs. [:- a, 1:]) = (∏a←rs'. [:- a, 1:])"
  shows "mset rs = mset rs'"
proof-
  have "(∏a←rs. [:- a, 1:]) = (∏a←(map uminus rs). [:a, 1:])"
    by (simp add: o_def pCons_def)
  moreover have "(∏a←rs'. [:- a, 1:]) = (∏a←(map uminus rs'). [:a, 1:])"
    by (simp add: o_def pCons_def)
  ultimately have "mset (map uminus rs) = mset (map uminus rs')"
    using idom_poly_factor_unique[of "map uminus rs" "map uminus rs'"] eq by argo
  moreover have "mset (map uminus (map uminus (as::'a list))) = mset as" for as by simp
  ultimately show ?thesis using mset_map by metis
qed

section "Vector Normalization"

lemma vec_normalize_norm: "v ∈ carrier_vec n ⟹ v ≠ 0v n ⟹ vec_norm (vec_normalize v) = 1"
  by (simp add: normalized_vec_norm vec_norm_def)

section "Determinant, Invertability, and Eigenvalues"

definition eigvals_of [simp]:
  "eigvals_of M es ⟷ char_poly M = (∏a←es. [:- a, 1:]) ∧ length es = dim_row M"

lemma eigvals_of_mset_eq:
    "eigvals_of (A::'a::idom mat) es ⟹ eigvals_of A es' ⟹ mset es = mset es'"
  unfolding eigvals_of by (metis idom_poly_factor_unique')

lemma det_is_prod_of_eigenvalues:
  fixes A :: "complex mat"
  assumes "square_mat A"
  shows "Determinant.det A = (∏e ← (eigvals A). e)"
proof-
  define es where "es ≡ eigvals A"
  define n where "n ≡ dim_row A"
  have 1: "A ∈ carrier_mat n n" using assms n_def by fastforce
  have 2: "char_poly A = (∏e ← es. [:- e, 1:])"
    unfolding es_def eigvals_def
    using 1
    by (metis (mono_tags, lifting) eigvals_poly_length someI_ex)
  obtain Q Q' B where *: "similar_mat_wit A B Q Q' ∧ upper_triangular B ∧ diag_mat B = es"
    using schur_decomposition[OF 1 2] by (metis surj_pair)

  then have "Determinant.det A = Determinant.det (Q * B * Q')" unfolding similar_mat_wit_def by metis
  also have "… = Determinant.det Q * Determinant.det B * Determinant.det Q'"
    by (smt (verit, ccfv_SIG) "*" "1" det_mult mult_carrier_mat similar_mat_witD2(5) similar_mat_witD2(6) similar_mat_witD2(7))
  also have "… = Determinant.det Q * Determinant.det B * 1/(Determinant.det Q)"
    by (smt (verit, ccfv_threshold) "*" "1" det_mult det_one div_by_0 helper mult_cancel_left1 n_def nonzero_mult_div_cancel_left similar_mat_witD(6) similar_mat_witD(7) similar_mat_witD2(1))
  also have "… = Determinant.det Q * (∏e ← diag_mat B. e) * 1/(Determinant.det Q)"
    by (metis "*" det_upper_triangular list.map_ident similar_mat_witD(5))
  also have "… = (∏e ← (eigvals A). e)"
    by (metis (no_types, lifting) * es_def "1" Groups.mult_ac(2) class_field.zero_not_one det_mult det_one mult_cancel_left2 nonzero_mult_div_cancel_left similar_mat_witD(6) similar_mat_witD(7) similar_mat_witD2(2))
  finally show ?thesis .
qed

lemma eigvals_of_spectrum:
    "(A::(complex mat)) ∈ carrier_mat n n ⟹ eigvals_of A α ⟹ Spectral_Radius.spectrum A = set α"
  unfolding eigvals_of
  using eigenvalue_root_char_poly[of A n]
  by (metis Spectral_Radius.spectrum_def equalityI linear_poly_root mem_Collect_eq root_poly_linear subsetI)

lemma spectrum_connect:
    "(A::complex mat) ∈ carrier_mat n n ⟹ Spectral_Radius.spectrum A = spectrum A"
  by (metis eigvals_of eigvals_of_spectrum eigvals_poly_length spectrum_def)

lemma spectrum_shift:
  fixes A :: "complex mat"
  assumes dim: "A ∈ carrier_mat n n"
  shows "spectrum (A - r ⋅m 1m n) = {μ - r | μ. μ ∈ spectrum A}" (is "?lhs = ?rhs")
proof
  { let ?A' = "A - r ⋅m 1m n"
    fix μ assume μ: "μ ∈ ?lhs"
    then obtain v where v: "v ∈ carrier_vec n" "v ≠ 0v n" "?A' *v v = μ ⋅v v"
      by (metis (no_types, lifting) carrier_mat_triv eigenvalue_def eigenvector_def index_minus_mat(2,3)
          index_one_mat(2,3) index_smult_mat(2,3) spectrum_eigenvalues)
    hence "?A' *v v = A *v v - r ⋅v v"
      using dim
      by (smt (verit, best) minus_mult_distrib_mat_vec one_carrier_mat one_mult_mat_vec
          smult_carrier_mat smult_mat_mult_mat_vec_assoc)
    hence "A *v v = μ ⋅v v + r ⋅v v" using v(1,3) dim by auto
    also have "… = (μ + r) ⋅v v" using v(1) by (simp add: add_smult_distrib_vec)
    finally have "μ + r ∈ spectrum A"
      using spectrum_connect[OF dim] v(1,2) dim
      by (metis Spectral_Radius.spectrum_def carrier_matD(1) eigenvalue_def eigenvector_def mem_Collect_eq)
    hence "μ ∈ ?rhs" by force
  }
  thus "?lhs ⊆ ?rhs" by blast
next
  { let ?A' = "A - r ⋅m 1m n"
    fix μ assume μ: "μ ∈ ?rhs"
    then obtain μ' where μ': "μ = μ' - r" "μ' ∈ spectrum A" by blast
    then obtain v where v: "v ∈ carrier_vec n" "v ≠ 0v n" "A *v v = μ' ⋅v v"
      using spectrum_connect[OF dim] dim
      by (metis carrier_matD(1) eigenvalue_def eigenvector_def spectrum_eigenvalues)
    have "?A' *v v = A *v v - r ⋅v v"
      using v(1) dim
      by (smt (verit, del_insts) minus_mult_distrib_mat_vec one_carrier_mat one_mult_mat_vec
          smult_carrier_mat smult_mat_mult_mat_vec_assoc)
    also have "… = μ' ⋅v v - r ⋅v v" using v(3) by auto
    also have "… = μ ⋅v v" using dim v(1) μ'(1) by (simp add: minus_smult_vec_distrib)
    finally have "μ ∈ spectrum ?A'"
      using v(1,2) spectrum_connect[OF dim]
      by (smt (verit) Spectral_Radius.spectrum_def carrier_mat_def eigenvalue_def eigenvector_def
          index_minus_mat(2,3) index_one_mat(2,3) index_smult_mat(2,3) mem_Collect_eq spectrum_connect)
    hence "μ ∈ ?lhs" by blast
  }
  thus "?rhs ⊆ ?lhs" by blast
qed

lemma trivial_kernel_imp_nonzero_eigenvalues:
  fixes M :: "'a::{idom,ring_1_no_zero_divisors} mat"
  assumes "square_mat M"
  assumes "mat_kernel M ⊆ {0v (dim_row M)}"
  assumes "eigenvalue M e"
  shows "e ≠ 0"
  using assms by (metis (no_types, lifting) carrier_matI carrier_vecD eigenvalue_def eigenvector_def empty_iff mat_kernelI singleton_iff smult_vec_zero square_mat.simps subset_singletonD)

lemma trivial_kernel_imp_invertible: 
  fixes M :: "complex mat"
  assumes "square_mat M"
  assumes "mat_kernel M ⊆ {0v (dim_row M)}"
  shows "invertible_mat M"
  using assms by (metis carrier_matI det_0_iff_vec_prod_zero_field empty_iff invertible_det mat_kernelI singletonD square_mat.elims(2) subset_singletonD)

lemma trivial_kernel_imp_det_nz:
  fixes M :: "complex mat"
  assumes "square_mat M"
  assumes "mat_kernel M ⊆ {0v (dim_row M)}"
  shows "Determinant.det M ≠ 0"
  using trivial_kernel_imp_invertible[OF assms(1) assms(2)]
  using invertible_det assms(1) square_mat.simps
  by blast

lemma similar_mats_eigvals:
  assumes "A ∈ carrier_mat n n"
  assumes "B ∈ carrier_mat n n"
  assumes "similar_mat A B"
  assumes "eigvals_of A es"
  shows "eigvals_of B es"
  using assms unfolding eigvals_of
  by (metis (no_types) char_poly_similar assms(1-3) carrier_matD(1))

lemma scale_eigvals:
  fixes A :: "'a::conjugatable_ordered_field mat"
  assumes "square_mat A"
  assumes "B = c ⋅m A"
  assumes "eigvals_of A es"
  shows "eigvals_of B (map (λx. c * x) es)"
proof-
  obtain A' P Q where A_decomp: "schur_decomposition A es = (A', P, Q)
      ∧ similar_mat_wit A A' P Q
      ∧ upper_triangular A'
      ∧ diag_mat A' = es"
    using assms(1,3) unfolding eigvals_of square_mat.simps
    by (metis carrier_mat_triv old.prod.exhaust schur_decomposition)
  define B' where "B' ≡ c ⋅m A'"

  have B'_square: "square_mat B'" using A_decomp B'_def similar_mat_witD(5) by fastforce
  have B_decomp: "similar_mat_wit B B' P Q ∧ upper_triangular B'"
  proof-
    have "upper_triangular B'"
    proof-
      { fix i j assume *: "j < i" "i < dim_row B'"
        hence "B'$$(i,j) = c * A'$$(i,j)" using B'_def B'_square by auto
        also have "… = 0" using A_decomp * unfolding upper_triangular_def by (simp add: B'_def)
        finally have "B'$$(i,j) = 0" .
      }
      thus ?thesis by blast
    qed
    moreover have "similar_mat_wit B B' P Q"
    proof-
      have "B = c ⋅m (P * A' * Q)" using A_decomp assms(2) similar_mat_witD2(3) by blast
      also have "… = P * (c ⋅m A') * Q"
        by (metis A_decomp similar_mat_wit_def similar_mat_wit_smult)
      also have "… = P * B' * Q" using B'_def by argo
      finally have "B = P * B' * Q" .
      thus ?thesis by (smt (verit, best) A_decomp B'_def assms(2) similar_mat_wit_smult)
    qed
    ultimately show ?thesis by blast
  qed

  hence "char_poly B' = (∏a←diag_mat B'. [:- a, 1:])"
    using char_poly_upper_triangular B_decomp B'_square by auto
  moreover have "length (diag_mat B') = dim_row B'" by (simp add: diag_mat_length)
  ultimately have "eigvals_of B' (diag_mat B')" using eigvals_of by blast
  moreover have "diag_mat B' = map (λx. c * x) es"
    using A_decomp B'_def by (metis diag_mat_map similar_mat_witD(5) smult_mat_def)
  ultimately show ?thesis                                 
    using similar_mats_eigvals B_decomp assms(2) assms(3) char_poly_similar similar_mat_def
    by fastforce
qed

lemma neg_mat_eigvals:
  fixes A :: "complex mat"
  assumes "square_mat A"
  assumes "eigvals_of A es"
  shows "eigvals_of (-A) (rev (map (λx. -x) es))"
proof-
  have "eigvals_of A (rev es)"
    using assms(2)
    unfolding eigvals_of
    by (metis length_rev prod_list.rev rev_map)
  thus ?thesis
    using scale_eigvals[of A "-A" "-1" "rev es"]
    by (metis (lifting) ext assms(1) carrier_mat_triv mult_commute_abs mult_minus1_right rev_map square_mat.elims(2) uminus_mat)
qed

section "Quadratic Form"

definition quadratic_form :: "'a mat ⇒ 'a vec ⇒ 'a::{conjugatable_ring}" where
  "quadratic_form M x ≡ inner_prod x (M *v x)"

abbreviation "QF ≡ quadratic_form"

declare
  quadratic_form_def[simp]

section "Leading Principal Submatrix"

definition leading_principal_submatrix :: "'a mat ⇒ nat ⇒ 'a mat" where
  [simp]: "leading_principal_submatrix A k = submatrix A {..<k} {..<k}"

abbreviation "lps ≡ leading_principal_submatrix"

lemma leading_principal_submatrix_carrier:
  "m ≥ n ⟹ A ∈ carrier_mat m m ⟹ lps A n ∈ carrier_mat n n"
proof-
  assume *: "m ≥ n" "A ∈ carrier_mat m m"
  let ?B = "lps A n"
  have "(card {i. i < dim_row A ∧ i ∈ {..<n}}) = n"
    by (metis "*"(1) "*"(2) Collect_conj_eq Collect_mem_eq card_lessThan carrier_matD(1) inf.absorb_iff2 lessThan_def lessThan_subset_iff)
  hence "dim_col ?B = n ∧ dim_row ?B = n"
    unfolding leading_principal_submatrix_def submatrix_def
    using "*"(2) by auto
  thus ?thesis by blast
qed

lemma pick_n:
  assumes "i ≤ n"
  shows "pick {..n} i = i"
  using assms
proof(induct i)
  case 0
  then show ?case by force
next
  case (Suc i)
  hence "Suc i ∈ {..n}" by blast
  moreover from Suc have "Suc i > pick {..n} i" by simp
  moreover from Suc have "∀i' < Suc i. ¬ (i'∈{..n} ∧ i' > pick {..n} i)"
    using Suc_leD not_less_eq by presburger
  ultimately have "Suc i = (LEAST a. a∈{..n} ∧ a > pick {..n} i)"
    by (metis (no_types, lifting) LeastI linorder_not_less not_less_Least order.strict_iff_order)
  thus ?case by (metis DL_Missing_Sublist.pick.simps(2))
qed

lemma pick_n_le:
  assumes "i < n"
  shows "pick {..<n} i = i"
  by (metis assms lessThan_Suc_atMost less_Suc_eq_le not0_implies_Suc not_less_zero pick_n)

lemma leading_principal_submatrix_index:
  assumes "A ∈ carrier_mat n n"
  assumes "k ≤ n"
  assumes "i < k"
  assumes "j < k"
  shows "(lps A k)$$(i,j) = A$$(i,j)"
proof-
  have "⋀i. i < k ⟹ pick {..<k} i = i" by (simp add: pick_n_le)
  moreover have "card {i. i < dim_row A ∧ i ∈ {..<k}} = k"
    by (metis Collect_conj_eq Collect_mem_eq assms(1) assms(2) card_lessThan carrier_matD(1) inf.absorb_iff2 lessThan_def lessThan_subset_iff)
  moreover then have "card {j. j < dim_col A ∧ j ∈ {..<k}} = k" using assms(1) by force
  moreover have "(mat k k (λ(i, j). A$$(i,j)))$$(i,j) = A$$(i,j)" using assms(3) assms(4) by auto
  ultimately show ?thesis by (simp add: assms(3) assms(4) submatrix_def)
qed

lemma nested_leading_principle_submatrices:
  assumes "A ∈ carrier_mat n n"
  assumes "k1 ≤ k2"
  assumes "k2 ≤ n"
  shows "lps A k1 = lps (lps A k2) k1" (is "?lhs = ?rhs")
proof-
  have "⋀i j. i < k1 ⟹ j < k1 ⟹ ?lhs$$(i,j) = ?rhs$$(i,j)"
    by (smt (verit, best) assms dual_order.trans leading_principal_submatrix_carrier leading_principal_submatrix_index order.strict_trans2)
  moreover have "?lhs ∈ carrier_mat k1 k1"
    by (meson assms leading_principal_submatrix_carrier order_trans)
  moreover have "?rhs ∈ carrier_mat k1 k1"
    by (meson assms leading_principal_submatrix_carrier)
  ultimately show ?thesis by auto
qed

section "Hermitian Matrix"

(* TODO: put into cmplx_herm_mat locale *)
lemma hermitian_real_diag_decomp_eigvals:
  fixes A :: "complex mat"
  assumes "A ∈ carrier_mat n n"
  assumes "hermitian A"
  assumes "eigvals_of A es"
  obtains B U where
      "real_diag_decomp A B U"
      "diag_mat B = es"
      "set es ⊆ Reals"
      "B ∈ carrier_mat n n"
      "U ∈ carrier_mat n n"
proof-
  (* Yoinked from the existing proof of
    hermitian_real_diag_decomp in Spectral_Theory_Complements.hermitian_real_diag_decomp *)
  have es: "char_poly A = (∏ (e :: complex) ← es. [:- e, 1:])" 
    using assms eigvals_poly_length by auto
  obtain B U Q where us: "unitary_schur_decomposition A es = (B,U,Q)" 
    by (cases "unitary_schur_decomposition A es")
  hence *: "similar_mat_wit A B U (adjoint U) ∧ diagonal_mat B
      ∧ diag_mat B = es ∧ unitary U ∧ (∀i < n. B$$(i, i) ∈ Reals)" 
    using hermitian_eigenvalue_real assms es by auto
  moreover then have "dim_row B = n" using assms similar_mat_wit_dim_row[of A] by auto
  ultimately have 1: "real_diag_decomp A B U" using unitary_diagI[of A] 
    unfolding real_diag_decomp_def by simp

  from * have 2: "diag_mat B = es" by blast
  from * have 3: "set es ⊆ Reals" by (metis ‹dim_row B = n› diag_elems_real diag_elems_set_diag_mat)
  from * have 4: "B ∈ carrier_mat n n" by (meson assms(1) similar_mat_witD2(5))
  from * have 5: "U ∈ carrier_mat n n" by (meson assms(1) similar_mat_witD2(6))

  from that show ?thesis using 1 2 3 4 5 by blast
qed

lemma hermitian_eigvals_of_real: "hermitian (A::complex mat) ⟹ eigvals_of A es ⟹ set es ⊆ ℝ"
  using hermitian_real_diag_decomp_eigvals[of A _ es] hermitian_square by blast

lemma hermitian_is_square: "hermitian A ⟹ square_mat A"
  by (metis adjoint_dim_col hermitian_def square_mat.simps)

lemma hermitian_ij_ji_iff:
  "hermitian A
    ⟷ square_mat A ∧ (∀i j. i < dim_row A ∧ j < dim_row A ⟶ A$$(i,j) = conjugate (A$$(j,i)))"
  by (metis (no_types, lifting) adjoint_dim_col adjoint_dim_row adjoint_eval hermitian_def mat_eq_iff square_mat.simps)

lemma adjoint_is_conjugate_transpose: "AH = adjoint A" 
  by (simp add: adjoint_def transpose_def cong_mat conjugate_mat_def)

subsection "Locale for Complex Hermitian Matrices"

locale cmplx_herm_mat = complex_vec_space n for n +
  fixes A :: "complex mat"
  assumes A_dim: "A ∈ carrier_mat n n"
  assumes A_herm: "hermitian A"
begin

lemma hermitian_quadratic_form_real:
  fixes v :: "complex vec"
  assumes v: "v ∈ carrier_vec n"
  shows "QF A v ∈ Reals"
proof-
  have "conjugate (QF A v) = inner_prod (A *v v) v"
    using A_dim v by (metis inner_prod_swap mult_mat_vec_carrier quadratic_form_def)
  also have "… = inner_prod v ((adjoint A) *v v)"
    using A_dim A_herm v by (metis adjoint_def_alter hermitian_def)
  also have "… = inner_prod v (A *v v)" using A_herm by (simp add: hermitian_def)
  finally have "conjugate (QF A v) = QF A v" by simp
  thus ?thesis by (simp add: Reals_cnj_iff)
qed

definition eigenvalues0 :: "complex list" where
  "eigenvalues0 ≡ eigvals A"

lemma eigenvalues0_exist: "∃es. eigvals_of A es" using A_dim eigvals_poly_length by auto

lemma eigenvalues0: "eigvals_of A eigenvalues0"
  using eigenvalues0_exist unfolding eigvals_of eigenvalues0_def eigvals_def ..

lemma eigenvalues0_real: "set eigenvalues0 ⊆ ℝ"
  using A_herm eigenvalues0 hermitian_eigvals_of_real
  by blast

definition eigenvalues :: "complex list" where
  "eigenvalues ≡ rev (sort_key Re eigenvalues0)"

lemma eigenvalues: "eigvals_of A eigenvalues"
proof-
  have "mset eigenvalues = mset eigenvalues0" by (simp add: eigenvalues_def)
  thus ?thesis
    using eigenvalues_def eigvals_of eigenvalues0
    by (metis (mono_tags, lifting) mset_eq_length mset_map prod_mset_prod_list)
qed

lemma eigenvalues_sorted: "sorted_wrt (≥) eigenvalues"
proof-
  have "∀μ1 μ2. μ1 ∈ set eigenvalues0 ⟶ μ2 ∈ set eigenvalues0 ⟶ (μ1 ≤ μ2 ⟷ Re μ1 ≤ Re μ2)"
    using eigenvalues_def eigenvalues0_real by (simp add: complex_is_Real_iff less_eq_complex_def subset_iff)
  hence "sorted_wrt (≤) (sort_key Re eigenvalues0)"
    by (smt (verit, ccfv_threshold) length_map nth_map nth_mem order_trans_rules(19) set_sort sorted_sort_key
        sorted_wrt_iff_nth_less)
  thus ?thesis unfolding eigenvalues_def sorted_wrt_rev .
qed

lemma eigenvalues_unique:
  assumes es: "eigvals_of A es'"
  assumes es_sorted: "sorted_wrt (≥) es'"
  shows "es' = eigenvalues"
proof-
  have "mset es' = mset eigenvalues" using es eigenvalues eigvals_of_mset_eq by blast
  moreover have "length es' = length eigenvalues" by (metis calculation size_mset)
  moreover have "sorted (rev (map Re es'))"
    using es_sorted by (metis less_eq_complex_def sorted_wrt_map_mono sorted_wrt_rev)
  moreover have "sorted (rev (map Re eigenvalues))"
    using eigenvalues by (simp add: eigenvalues_def rev_map)
  ultimately have "rev (map Re es') = rev (map Re eigenvalues)"
    by (metis mset_map mset_rev properties_for_sort) 
  hence "map Re es' = map Re eigenvalues" by force
  moreover have "set es' ⊆ ℝ ∧ set eigenvalues ⊆ ℝ"
    using es A_herm eigenvalues hermitian_eigvals_of_real by blast 
  ultimately show "es' = eigenvalues" by (metis list.inj_map_strong of_real_Re subset_code(1))
qed

lemma eigenvalues_real: "set eigenvalues ⊆ ℝ"
  using A_herm hermitian_eigvals_of_real eigenvalues(1) by blast

lemma eigenvalues_set_eq: "set eigenvalues0 = set eigenvalues"
  by (simp add: eigenvalues_def)

lemma hermitian_eigenvalues_real:
  assumes e: "eigenvalue A e"
  shows "e ∈ Reals"
  using cpx_sq_mat.hermitian_spectrum_real[OF _ A_dim A_herm, of n n e]
  using A_dim e
  by (metis Projective_Measurements.spectrum_def cpx_sq_mat_axioms.intro cpx_sq_mat_def eigenvalue_imp_nonzero_dim eigenvalue_root_char_poly eigvals_poly_length fixed_carrier_mat.intro root_poly_linear)

lemma hermitian_spectrum_real: "spectrum A ⊆ Reals"
  unfolding spectrum_connect[OF A_dim, symmetric]
  by (simp add: Spectral_Radius.spectrum_def hermitian_eigenvalues_real unfold_simps(2))

lemma leading_principal_submatrix_hermitian:
  assumes k: "k ≤ n"
  shows "hermitian (lps A k)" (is "hermitian ?A'")
proof-
  have "⋀i j. i < dim_row ?A' ⟹ j < dim_col ?A' ⟹ ?A'$$(i,j) = conjugate (?A'$$(j,i))"
    using k A_dim A_herm
    by (metis (no_types, lifting) adjoint_eval carrier_matD(1) carrier_matD(2) dual_order.strict_trans1 hermitian_def leading_principal_submatrix_carrier leading_principal_submatrix_index)
  thus ?thesis
    using A_dim k
    by (metis (no_types, lifting) adjoint_dim_col adjoint_dim_row adjoint_eval carrier_matD(1) carrier_matD(2) eq_matI hermitian_def leading_principal_submatrix_carrier)
qed

lemma hermitian_mat_inv:
  assumes A'_dim: "A' ∈ carrier_mat n n"
  assumes inv: "inverts_mat A A'"
  shows "hermitian A'"
  using A_dim A_herm assms
  by (metis (no_types, lifting) ext adjoint_dim' adjoint_mult adjoint_one
      carrier_matD(1,2) hermitian_def invertible_det invertible_mat_def inverts_mat_def inverts_mat_sym
      square_mat.simps vec_space.det_nonzero_congruence)

lemma negative_hermitian: "hermitian (-A)"
  using A_dim A_herm
  by (metis hermitian_minus left_add_zero_mat minus_add_uminus_mat uminus_carrier_iff_mat zero_carrier_mat zero_hermitian)

lemma principal_submatrix_hermitian:
  assumes I: "I ⊆ {..<n}"
  shows "hermitian (submatrix A I I)" (is "hermitian ?B")
proof-
  have "square_mat ?B"
    using A_dim
    by (metis (full_types) carrier_matD(1) carrier_matD(2) dim_submatrix(1) dim_submatrix(2) square_mat.elims(1))
  moreover {
    fix i j assume *: "i < dim_row ?B ∧ j < dim_row ?B"
    then obtain i' j' where "?B$$(i,j) = A$$(i',j') ∧ i' = pick I i ∧ j' = pick I j"
      unfolding submatrix_def using A_dim pick_le by auto
    moreover then have "?B$$(j,i) = A$$(j',i')"
      unfolding submatrix_def
      using A_dim
      by (metis (no_types, lifting) Collect_cong * carrier_matD(1) carrier_matD(2) case_prod_conv dim_submatrix(1) index_mat(1))
    ultimately have "?B$$(i,j) = conjugate (?B$$(j,i))"
      using * A_herm by (metis dim_submatrix(1) hermitian_ij_ji_iff pick_le)
  }
  ultimately show ?thesis by (metis hermitian_ij_ji_iff)
qed

lemma hermitian_row_col:
  assumes "i < n"
  shows "row A i = conjugate (col A i)"
  using A_dim A_herm assms by (metis adjoint_row carrier_matD(2) hermitian_def)

lemma inner_prod_swap:
  assumes v: "v ∈ carrier_vec n"
  assumes w: "w ∈ carrier_vec n"
  shows "(A *v v) ∙c w = v ∙c (A *v w)"
proof-
  have "(A *v v) ∙c w = v ∙c (AH *v w)"
    using assms by (metis adjoint_def_alter adjoint_is_conjugate_transpose A_dim)
  also have "… = v ∙c (A *v w)"
    using A_herm assms by (simp add: adjoint_is_conjugate_transpose hermitian_def)
  finally show ?thesis .
qed

end

section "Matrix Conjugate"

lemma conjugate_mat_dist:
  fixes A B :: "'a::conjugatable_ring mat"
  assumes "A ∈ carrier_mat m n"
  assumes "B ∈ carrier_mat n p"
  shows "(conjugate A) * (conjugate B) = conjugate (A * B)"
  using assms
  by (smt (z3) carrier_matD(1) carrier_matD(2) col_conjugate conjugate_scalar_prod dim_col dim_col_conjugate dim_row_conjugate eq_matI index_mult_mat(1) index_mult_mat(2) index_mult_mat(3) index_row(2) mat_index_conjugate row_conjugate)

lemma conjugate_mat_inv:
  fixes A :: "'a::{conjugatable_ring,semiring_1} mat"
  assumes "A ∈ carrier_mat n n"
  assumes "A' ∈ carrier_mat n n"
  assumes "inverts_mat A A'"
  shows "inverts_mat (conjugate A) (conjugate A')"
proof-
  have "(conjugate A) * (conjugate A') = conjugate (A * A')"
    using conjugate_mat_dist assms(1) assms(2) by blast
  also have "… = conjugate (1m n)"
    by (metis assms(1) assms(3) carrier_matD(1) inverts_mat_def)
  also have "… = 1m n"
    by (metis (no_types, lifting) carrier_matI conjugate_id conjugate_mat_dist dim_col_conjugate dim_row_conjugate index_one_mat(2) index_one_mat(3) left_mult_one_mat' right_mult_one_mat')
  finally show ?thesis
    by (metis index_mult_mat(2) index_one_mat(2) inverts_mat_def)
qed

lemma conjugate_dist_mult_mat:
  fixes A :: "'a::conjugatable_ring mat"
  assumes "A ∈ carrier_mat m n" "B ∈ carrier_mat n p"
  shows "conjugate (A * B) = conjugate A * conjugate B"
    (is "?lhs = ?rhs")
proof-
  have "⋀i j. i < m ⟹ j < p ⟹ ?lhs$$(i,j) = ?rhs$$(i,j)"
    by (smt (verit, del_insts) assms carrier_matD(1) carrier_matD(2) col_conjugate conjugate_scalar_prod dim_col dim_col_conjugate dim_row_conjugate index_mult_mat(1) index_mult_mat(2) index_mult_mat(3) index_row(2) mat_index_conjugate row_conjugate)  
  moreover have "?lhs ∈ carrier_mat m p" using assms by auto
  ultimately show ?thesis using assms carrier_matD(2) by auto
qed

lemma conjugate_dist_add_mat:
  fixes A :: "'a::conjugatable_ring mat"
  assumes "A ∈ carrier_mat m n" "B ∈ carrier_mat m n"
  shows "conjugate (A + B) = conjugate A + conjugate B"
    (is "?lhs = ?rhs")
proof-
  have "⋀i j. i < m ⟹ j < n ⟹ ?lhs$$(i,j) = ?rhs$$(i,j)"
    using assms assms conjugate_dist_add by fastforce
  moreover have "?lhs ∈ carrier_mat m n" using assms by auto
  ultimately show ?thesis using assms carrier_matD(2) by auto
qed

lemma mat_row_conj:
  assumes "A ∈ carrier_mat m n"
  assumes "i < m"
  shows "conjugate (row A i) = row (conjugate A) i"
  using assms
  unfolding conjugate_mat_def
  by auto

lemma conj_mat_vec_mult:
  fixes A :: "'a::{conjugate,conjugatable_ring} mat"
  fixes v :: "'a vec"
  assumes "A ∈ carrier_mat n n"
  assumes "v ∈ carrier_vec n"
  shows "conjugate (A *v v) = (conjugate A) *v (conjugate v)"
    (is "?lhs = ?rhs")
proof-                            
  have "⋀i. i < n ⟹ ?lhs$i = ?rhs$i"
    by (metis assms carrier_matD(1) conjugate_sprod_vec dim_mult_mat_vec dim_row_conjugate index_mult_mat_vec mat_row_conj row_carrier_vec vec_index_conjugate)
  moreover have "?lhs ∈ carrier_vec n" using assms by force
  ultimately show ?thesis using assms(1) by auto
qed

lemma conjugate_vec_first:
  assumes "v ∈ carrier_vec n"
  assumes "i ≤ n"
  shows "conjugate (vec_first v i) = vec_first (conjugate v) i"
  by (smt (verit, ccfv_SIG) assms carrier_vecD dim_vec_conjugate dim_vec_first eq_vecI index_vec le_less less_trans vec_first_def vec_index_conjugate)

lemma conjugate_vec_last: "i ≤ dim_vec v ⟹ conjugate (vec_last v i) = vec_last (conjugate v) i"
  unfolding vec_last_def by auto

lemma cscalar_prod_symm_conj:
  "dim_vec (x::('a::{comm_semiring_0,conjugatable_ring} vec)) = dim_vec (y::'a vec)
    ⟹ x ∙c y = conjugate (y ∙c x)"
  by (simp add: conjugate_scalar_prod scalar_prod_comm)

section "Block Matrix"

lemma block_mat_vec_mult:
  fixes x
  assumes "A ∈ carrier_mat nr1 nc1"
  assumes "B ∈ carrier_mat nr1 nc2"
  assumes "C ∈ carrier_mat nr2 nc1"
  assumes "D ∈ carrier_mat nr2 nc2"
  assumes "M = four_block_mat A B C D"
  assumes "x ∈ carrier_vec (nc1 + nc2)"
  defines "x1 ≡ vec_first x nc1"
  defines "x2 ≡ vec_last x nc2"
  shows "M *v x = (A *v x1 + B *v x2) @v (C *v x1 + D *v x2)"
  by (smt (verit, ccfv_threshold) assms four_block_mat_mult_vec vec_first_carrier vec_first_last_append vec_last_carrier)

lemma mat_vec_prod_leading_principal_submatrix:
  fixes A :: "('a :: comm_ring) mat"
  assumes "A ∈ carrier_mat (Suc n) (Suc n)"
  assumes "x ∈ carrier_vec (Suc n)"
  defines "An ≡ lps A n"
  defines "vn ≡ vec_first (col A n) n"
  defines "wn ≡ vec_first (row A n) n"
  defines "a ≡ A $$ (n, n)"
  defines "xn ≡ vec_first x n"
  defines "b ≡ x$n"
  shows "A *v x = (An *v xn + b ⋅v vn) @v (vec 1 (λi. (wn ∙ xn) + a * b))" (is "?lhs = ?rhs")
proof
  have dim_xn: "dim_vec xn = n" by (simp add: assms(7))
  have dim_wn: "dim_vec wn = n" by (simp add: assms(5))
  have dim_row_An: "dim_row An = n"
    by (metis assms(1) assms(3) carrier_matD(1) le_add2 leading_principal_submatrix_carrier plus_1_eq_Suc)
  have dim_col_An: "dim_col An = n"
    by (metis assms(1) assms(3) carrier_matD(2) le_add2 leading_principal_submatrix_carrier plus_1_eq_Suc)


  show dims: "dim_vec ?lhs = dim_vec ?rhs" using assms(1) vn_def by auto
  show "⋀i. i < dim_vec ?rhs ⟹ ?lhs$i = ?rhs$i"
  proof-
    fix i assume *: "i < dim_vec ?rhs"
    hence i: "i < Suc n" using dims assms(1) by auto
    hence dot: "?lhs$i = row A i ∙ x" using * dims by fastforce
    have row_j: "⋀j. j < Suc n ⟹ (row A i)$j = A$$(i,j)" using assms(1) i by force

    show "?lhs$i = ?rhs$i"
    proof(cases "i < n")
      case True
      have An: "⋀j. j < n ⟹ A$$(i,j) = An$$(i,j)"
        by (metis True assms(1) assms(3) le_add2 leading_principal_submatrix_index plus_1_eq_Suc)
      have An_row: "⋀j. j < n ⟹ An$$(i,j) = (row An i)$j"
        by (metis True assms(1) assms(3) carrier_matD(1) carrier_matD(2) index_row(1) le_add2 leading_principal_submatrix_carrier plus_1_eq_Suc)
      have "?lhs$i = (An *v xn + b ⋅v vn)$i"
      proof-
        have "?lhs$i = (∑j<Suc n. A$$(i,j) * x$j)"
          unfolding dot scalar_prod_def using row_j assms(2) atLeast0LessThan by force
        moreover have "(∑j<n. A$$(i,j) * x$j) = (∑j<n. An$$(i,j) * xn$j)"
          by (smt (verit) An assms(7) index_vec lessThan_iff sum.cong vec_first_def)
        moreover have "(∑j<n. An$$(i,j) * xn$j) = (An *v xn)$i"
        proof-
          have "(An *v xn)$i = (row An i) ∙ xn"
            by (metis True assms(1) assms(3) carrier_matD(1) index_mult_mat_vec le_add2 leading_principal_submatrix_carrier plus_1_eq_Suc)
          moreover have "⋀j. j < n ⟹ An$$(i,j) * xn$j = (row An i)$j * xn$j"
            using An_row by presburger
          ultimately show ?thesis
            unfolding scalar_prod_def using atLeast0LessThan dim_xn by fastforce
        qed
        moreover have "(A$$(i,n) * x$n) = (b ⋅v vn)$i"
        proof-
          have "x$n = b" unfolding b_def by blast
          moreover have "A$$(i,n) = vn$i"
            using assms(1) i by (simp add: True vec_first_def vn_def)
          moreover have "(b ⋅v vn)$i = b * (vn$i)" by (simp add: True vn_def)
          ultimately show ?thesis by (simp add: mult.commute)
        qed
        ultimately show ?thesis by (simp add: True assms(4))
      qed
      thus ?thesis by (simp add: True i vn_def)
    next
      case False
      hence *: "i = n" using i by linarith
      hence "?lhs$i = (row A n) ∙ x" using dot by blast
      also have "… = wn ∙ xn + a * b"
      proof-
        have "row A n = wn @v vec_last (row A n) 1"
          by (metis "*" wn_def assms(1) i row_carrier_vec semiring_norm(174) vec_first_last_append)
        moreover have "(vec_last (row A n) 1)$0 = (row A n)$n"
          by (metis "*" False assms(1) calculation carrier_matD(2) dim_wn gr0I i index_append_vec(1) index_append_vec(2) index_row(2) zero_less_diff)
        ultimately have "row A n = wn @v (vec 1 (λ_. A$$(n,n)))"
          by (smt (verit, best) "*" One_nat_def carrier_vecD dim_vec eq_vecI i index_vec less_Suc0 row_j vec_last_carrier)
        moreover have "x = xn @v (vec 1 (λ_. b))"
        proof
          show *: "dim_vec x = dim_vec (xn @v (vec 1 (λ_. b)))" by (simp add: assms(2) dim_xn)
          have "⋀i. i < Suc n ⟹ x$i = (xn @v (vec 1 (λ_. b)))$i"
          proof-
            fix i assume "i < Suc n"
            show "x$i = (xn @v (vec 1 (λ_. b)))$i"
              apply (cases "i = n")
               apply (simp add: append_vec_def assms(8) dim_xn)
              apply (simp add: append_vec_def)
              by (smt (verit, best) ‹i < Suc n› dim_xn index_vec less_antisym vec_first_def xn_def)
          qed
          thus "⋀i. i < dim_vec (xn @v vec 1 (λ_. b)) ⟹ x $ i = (xn @v vec 1 (λ_. b)) $ i"
            by (metis * assms(2) carrier_vecD)
        qed
        ultimately have "(row A n) ∙ x = (wn ∙ xn) + ((vec 1 (λ_. A$$(n,n))) ∙ (vec 1 (λ_. b)))"
          by (metis assms(5) assms(7) scalar_prod_append vec_carrier vec_first_carrier)
        moreover have "((vec 1 (λ_. A$$(n,n))) ∙ (vec 1 (λ_. b))) = a * b"
          by (simp add: a_def b_def scalar_prod_def)
        ultimately show ?thesis by argo
      qed
      finally show ?thesis by (simp add: * vn_def)
    qed
  qed
qed

lemma vec_first_index: "n ≤ dim_vec v ⟹ i < n ⟹ v$i = (vec_first v n)$i"
  unfolding vec_first_def by simp

lemma vec_last_index:
    "n ≤ dim_vec v ⟹ i ∈ {dim_vec v - m..<m} ⟹ v$i = (vec_last v m)$(i - (dim_vec v - m))"
  unfolding vec_last_def by auto

lemma inner_prod_append:
  assumes "x ∈ carrier_vec (dim_vec (u @v v))"
  shows "x ∙c (u @v v) = (vec_first x (dim_vec u)) ∙c u + (vec_last x (dim_vec v)) ∙c v"
        "(u @v v) ∙c x = u ∙c (vec_first x (dim_vec u)) + v ∙c (vec_last x (dim_vec v))"
proof-
  define n where "n ≡ dim_vec (u @v v)"
  define nu where "nu ≡ dim_vec u"
  define nv where "nv ≡ dim_vec v"

  have dims_add: "nu + nv = n" by (simp add: nu_def nv_def n_def)

  have nu_prop: "⋀i. i < nu ⟹ conjugate (u @v v)$i = (conjugate u)$i" by (simp add: nu_def)
  have nv_prop: "⋀i. i < nv ⟹ conjugate (u @v v)$(i + nu) = (conjugate v)$i"
    by (simp add: nu_def nv_def)

  have "n = dim_vec (conjugate (u @v v))" by (simp add: n_def)
  hence "x ∙c (u @v v) = (∑i ∈ {0..<n}. x$i * (conjugate (u @v v))$i)"
    unfolding scalar_prod_def by blast
  hence "x ∙c (u @v v) =
      (∑i ∈ {0..<nu}. x$i * (conjugate (u @v v))$i)
      + (∑i ∈ {nu..<n}. x$i * (conjugate (u @v v))$i)"
    by (smt (verit, best) bot_nat_0.extremum index_append_vec(2) nu_def n_def nat_le_linear nless_le not_add_less1 sum.atLeastLessThan_concat)
  moreover have "(∑i ∈ {0..<nu}. x$i * (conjugate (u @v v))$i) = (vec_first x (dim_vec u)) ∙c u"
  proof-
    have *: "⋀i. i ∈ {0..<nu} ⟹ x$i = (vec_first x nu)$i"
      by (simp add: vec_first_def)
    have "(∑i ∈ {0..<nu}. x$i * (conjugate (u @v v))$i) = (∑i ∈ {0..<nu}. x$i * (conjugate u)$i)"
      using nu_prop by simp
    also have "… = (∑i ∈ {0..<nu}. (vec_first x nu)$i * (conjugate u)$i)"
      using * by auto
    also have "… = (vec_first x (dim_vec u)) ∙c u"
      by (metis (no_types, lifting) dim_vec_conjugate nu_def scalar_prod_def sum.cong)
    finally show ?thesis .
  qed
  moreover have "(∑i ∈ {nu..<n}. x$i * (conjugate (u @v v))$i) = (vec_last x (dim_vec v)) ∙c v"
  proof-
    have *: "vec_last (u @v v) nv = v"
      by (metis append_vec_eq carrier_vecI dims_add nu_def n_def vec_first_carrier vec_first_last_append)
    have "⋀i. i ∈ {nu..<n} ⟹ x$i = (vec_last x nv)$(i - (n - nv))"
      unfolding vec_last_def using assms dims_add less_diff_conv2 n_def by simp
    moreover have "⋀i. i ∈ {nu..<n}
        ⟹ (conjugate (u @v v))$i = (vec_last (conjugate (u @v v)) nv)$(i - (n - nv))"
      unfolding vec_last_def using assms dims_add less_diff_conv2 n_def by simp
    ultimately have "(∑i ∈ {nu..<n}. x$i * (conjugate (u @v v))$i)
        = (∑i ∈ {nu..<n}. (vec_last x nv)$(i - (n - nv))
          * (vec_last (conjugate (u @v v)) nv)$(i - (n - nv)))"
        (is "_ = (∑i ∈ _. ?F i)")
      by force
    also have "… = (∑i ∈ {0..<n-nu}. (vec_last x nv)$((i + nu) - (n - nv))
        * (vec_last (conjugate (u @v v)) nv)$((i + nu) - (n - nv)))"
      using sum.shift_bounds_nat_ivl[of ?F 0 nu nv] dims_add
      by (metis (no_types, lifting) add.commute add_0 add_diff_cancel_left')
    finally show ?thesis
      by (smt (verit, ccfv_SIG) "*" add_diff_cancel_left' add_diff_cancel_right' carrier_vecI conjugate_vec_last dim_vec_conjugate dims_add le_add2 nv_def n_def scalar_prod_def sum.cong)
  qed
  ultimately show "x ∙c (u @v v) = (vec_first x (dim_vec u)) ∙c u + (vec_last x (dim_vec v)) ∙c v"
    by argo

  (* Duplicating the proof allows us to avoid assuming that 'a is commutative *)
  have nu_prop: "⋀i. i < nu ⟹ (u @v v)$i = u$i" by (simp add: nu_def)
  have nv_prop: "⋀i. i < nv ⟹ (u @v v)$(i + nu) = v$i"
    by (simp add: nu_def nv_def)

  have "n = dim_vec (conjugate (u @v v))" by (simp add: n_def)
  moreover have "dim_vec (conjugate x) = n" using assms n_def by auto
  ultimately have "(u @v v) ∙c x = (∑i ∈ {0..<n}. (u @v v)$i * (conjugate x)$i)"
    unfolding scalar_prod_def by presburger
  hence "(u @v v) ∙c x =
      (∑i ∈ {0..<nu}. (u @v v)$i * (conjugate x)$i)
      + (∑i ∈ {nu..<n}. (u @v v)$i * (conjugate x)$i)"
    by (smt (verit, best) bot_nat_0.extremum index_append_vec(2) nu_def n_def nat_le_linear nless_le not_add_less1 sum.atLeastLessThan_concat)
  moreover have "(∑i ∈ {0..<nu}. (u @v v)$i * (conjugate x)$i) = u ∙c (vec_first x (dim_vec u))"
  proof-
    have *: "⋀i. i ∈ {0..<nu} ⟹ (conjugate x)$i = (vec_first (conjugate x) nu)$i"
      by (simp add: vec_first_def)
    have "(∑i ∈ {0..<nu}. (u @v v)$i * (conjugate x)$i)
        = (∑i ∈ {0..<nu}. u$i * (conjugate x)$i)"
      using nu_prop by simp
    also have "… = (∑i ∈ {0..<nu}. u$i * (vec_first (conjugate x) nu)$i)"
      using * by force
    also have "… = u ∙c (vec_first x (dim_vec u))"
      by (metis (no_types, lifting) add.commute assms conjugate_vec_first dim_vec_first dims_add le_add2 nu_def n_def scalar_prod_def sum.cong)
    finally show ?thesis .
  qed
  moreover have "(∑i ∈ {nu..<n}. (u @v v)$i * (conjugate x)$i) = v ∙c (vec_last x (dim_vec v))"
  proof-
    have *: "vec_last (u @v v) nv = v"
      by (metis append_vec_eq carrier_vecI dims_add nu_def n_def vec_first_carrier vec_first_last_append)
    have "⋀i. i ∈ {nu..<n} ⟹ (conjugate x)$i = (vec_last (conjugate x) nv)$(i - (n - nv))"
      unfolding vec_last_def using assms dims_add less_diff_conv2 n_def by simp
    moreover have "⋀i. i ∈ {nu..<n}
        ⟹ (u @v v)$i = (vec_last (u @v v) nv)$(i - (n - nv))"
      unfolding vec_last_def using assms dims_add less_diff_conv2 n_def by simp
    ultimately have "(∑i ∈ {nu..<n}. (u @v v)$i * (conjugate x)$i)
        = (∑i ∈ {nu..<n}. (vec_last (u @v v) nv)$(i - (n - nv))
          * (vec_last (conjugate x) nv)$(i - (n - nv)))"
        (is "_ = (∑i ∈ _. ?F i)")
      by force
    also have "… = (∑i ∈ {0..<n-nu}. (vec_last (u @v v) nv)$((i + nu) - (n - nv))
        * (vec_last (conjugate x) nv)$((i + nu) - (n - nv)))"
      using sum.shift_bounds_nat_ivl[of ?F 0 nu nv] dims_add
      by (metis (no_types, lifting) add.commute add_0 add_diff_cancel_left')
    finally show ?thesis
      by (smt (verit, best) "*" ‹dim_vec (conjugate x) = n› add_diff_cancel_left' add_diff_cancel_right' conjugate_vec_last dim_vec_conjugate dim_vec_last dims_add le_add2 scalar_prod_def sum.cong)
  qed
  ultimately show "(u @v v) ∙c x = u ∙c (vec_first x (dim_vec u)) + v ∙c (vec_last x (dim_vec v))"
    by argo
qed

subsection "Schur's Formula"

proposition schur_formula:
  fixes M :: "'a::field mat"
  assumes "(A,B,C,D) = split_block M r c"
  assumes "r < dim_row M"
  assumes "c < dim_col M"
  assumes "square_mat M"
  assumes "square_mat A"
  assumes "inverts_mat A' A"
  assumes A'_dim: "A' ∈ carrier_mat r r"
  shows "Determinant.det M = Determinant.det A * Determinant.det (D - C * A' * B)"
proof-
  let ?rM = "dim_row M"
  let ?cM = "dim_col M"
  let ?rA = "r"
  let ?cA = "c"
  let ?rB = "r"
  let ?cB = "?cM - ?cA"
  let ?rC = "?rM - ?rA"
  let ?cC = "c"
  let ?rD = "?rM - ?rA"
  let ?cD = "?cM - ?cA"
  let ?IA = "1m r"
  let ?ID = "1m ?rD"
  let ?OB = "0m ?rB ?cB"
  let ?OC = "0m ?rC ?cC"
  let ?P = "four_block_mat ?IA ?OB (C * A') ?ID"
  let ?Q = "four_block_mat A ?OB ?OC (D - C * A' * B)"
  let ?R = "four_block_mat ?IA (A' * B) ?OC ?ID"

  have M: "M = four_block_mat A B C D"
    using Matrix.split_block(5)[of M r c A B C D] by (metis assms(1-3) le_simps(1) less_eqE)

  have M_dim: "M ∈ carrier_mat ?rM ?cM"
    by blast
  have A_dim: "A ∈ carrier_mat r r"
    using assms(1)
    unfolding split_block_def
    by (metis Pair_inject assms(5) carrier_mat_triv dim_row_mat(1) square_mat.elims(2))
  have square: "?rM - ?rA = ?cM - ?cA" "r = c" "?rM = ?cM"
    using M_dim assms(4)
      apply (metis A_dim assms(1) assms(5) carrier_matD(1) dim_col_mat(1) prod.sel(1) split_block_def square_mat.elims(2))
     apply (metis A_dim assms(1) carrier_matD(2) dim_col_mat(1) prod.sel(1) split_block_def)
    using assms(4) by force
  have C_A'_dim: "C * A' ∈ carrier_mat ?rC ?cC"
    by (smt (verit) A'_dim Pair_inject assms(1) carrier_matD(2) carrier_mat_triv dim_row_mat(1) index_mult_mat(2) index_mult_mat(3) split_block_def square(2))
  have A'_B_dim: "A' * B ∈ carrier_mat ?rB ?cB"
    by (metis (no_types, lifting) A'_dim Pair_inject assms(1) carrier_matD(1) carrier_mat_triv dim_col_mat(1) index_mult_mat(2) index_mult_mat(3) split_block_def)
  have D_min_C_A'_B_dim: "D - C * A' * B ∈ carrier_mat ?cD ?cD"
    by (metis A'_B_dim C_A'_dim carrier_matD(1) carrier_matD(2) carrier_mat_triv index_mult_mat(2) index_mult_mat(3) minus_carrier_mat square(1))
  have P_dim: "?P ∈ carrier_mat ?rM ?cM"
    using assms(2) square(3) by auto
  have Q_dim: "?Q ∈ carrier_mat ?rM ?cM"
    by (smt (verit) A_dim D_min_C_A'_B_dim P_dim carrier_matD(1) carrier_matD(2) carrier_mat_triv index_mat_four_block(2) index_mat_four_block(3) index_one_mat(3) square(2) square(3))
  have R_dim: "?R ∈ carrier_mat ?rM ?cM"
    using P_dim by fastforce

  have M: "M = ?P * ?Q * ?R"
  proof-
    have B_dim: "B ∈ carrier_mat ?rB ?cB"
      by (metis assms(1) assms(2) assms(3) less_imp_le_nat ordered_cancel_comm_monoid_diff_class.add_diff_inverse split_block(2))
    have C_dim: "C ∈ carrier_mat ?rC r"
      by (metis assms(1) assms(3) diff_add_inverse less_eqE less_or_eq_imp_le split_block(3) square(2) square(3))
    have D_dim: "D ∈ carrier_mat ?rD ?cD"
      using A_dim M square(2) by auto

    have 1: "?IA ∈ carrier_mat r r" by simp
    have 2: "?OB ∈ carrier_mat ?rB ?cB" by auto
    have 3: "C * A' ∈ carrier_mat ?rC r" using C_A'_dim square(2) by blast
    have 4: "?ID ∈ carrier_mat ?rC ?cB" using "2" square(1) by auto
    have 6: "?OB ∈ carrier_mat r ?cB" using "2" square(2) by blast
    have 7: "?OC ∈ carrier_mat ?cB r" using "4" square(2) by auto
    have 8: "(D - C * A' * B) ∈ carrier_mat ?cB ?cB"
      using "4" D_min_C_A'_B_dim square(1) square(2) by auto
    have a: "(D - C * A' * B) ∈ carrier_mat ?rD ?cD" using "8" square(1) by presburger
    have b: "?ID ∈ carrier_mat ?cB ?cB" using "2" square(1) by auto
    have ass: "(C * A') * A = C * (A' * A)" using A_dim A'_dim C_dim by (simp add: square(2))
    have "?P * ?Q = four_block_mat A ?OB C (D - C * A' * B)"
    proof-
      have "A = ?IA * A + ?OB * ?OC"
        using A_dim square(2) square(3) by auto
      moreover have "?OB = ?IA * ?OB + ?OB * (D - C * A' * B)"
        by (smt (verit, ccfv_threshold) "1" "2" "8" D_min_C_A'_B_dim carrier_matD(2) left_add_zero_mat left_mult_zero_mat right_mult_zero_mat)
      moreover have "C = (C * A') * A + ?ID * ?OC"
        by (metis A'_dim C_dim square(2) Matrix.right_add_zero_mat ass assms(6) carrier_matD(1) index_zero_mat(2) inverts_mat_def left_mult_one_mat' right_mult_one_mat)
      moreover have "(D - C * A' * B) = ((C * A') * ?OB) + ?ID * (D - C * A' * B)"
        by (metis C_A'_dim D_min_C_A'_B_dim left_add_zero_mat left_mult_one_mat right_mult_zero_mat square(1) square(2))
      ultimately show ?thesis
        using mult_four_block_mat[OF 1 2 3 4 A_dim 6 7 8] by argo
    qed
    also have "… * ?R = four_block_mat A B C D"
    proof- (* ?R = "four_block_mat ?IA (A' * B) ?OC ?ID" *)
      have "A = A * ?IA + ?OB * C"
        using A_dim C_dim square(1) by force
      moreover have "B = A * (A' * B) + ?OB * ?ID"
        by (smt (verit, ccfv_threshold) inverts_mat_symm A_dim B_dim Matrix.right_add_zero_mat assms(6) assms(7) assoc_mult_mat b carrier_mat_triv index_mult_mat(2) inverts_mat_def left_mult_one_mat left_mult_zero_mat)
      moreover have "C = C * ?IA + (D - C * A' * B) * ?OC"
        by (metis "8" C_dim Matrix.right_add_zero_mat right_mult_one_mat right_mult_zero_mat square(1) square(2))
      moreover have "D = C * (A' * B) + (D - C * A' * B) * ?ID"
      proof-
        have "C * A' * B ∈ carrier_mat ?rD ?cD"
          using B_dim C_A'_dim mult_carrier_mat square(2) by blast
        moreover have "C * (A' * B) = C * A' * B" using B_dim C_dim assms(7) by force
        ultimately show ?thesis
          by (metis (no_types, lifting) "8" D_dim left_add_zero_mat mat_minus_minus minus_r_inv_mat right_mult_one_mat square(2) square(3))
      qed
      ultimately show ?thesis 
        using mult_four_block_mat[OF A_dim 2 C_dim a 1 A'_B_dim 7 b] A_dim square(2) square(3)
        by force
    qed
    also have "… = M" unfolding M by simp
    finally show ?thesis by argo
  qed
  hence "Determinant.det M = Determinant.det ?P * Determinant.det ?Q * Determinant.det ?R"
    by (smt (verit, best) det_mult P_dim Q_dim R_dim assms(4) mult_carrier_mat square_mat.elims(2))
  moreover have "Determinant.det ?P = 1"
    using det_four_block_mat_upper_right_zero[OF _ _ C_A'_dim, of ?IA ?OB ?ID]
    by (simp add: square)
  moreover have "Determinant.det ?Q = Determinant.det A * Determinant.det (D - C * A' * B)"
    using det_four_block_mat_upper_right_zero[OF A_dim _ _ D_min_C_A'_B_dim, of ?OB ?OC]
    by (simp add: square)
  moreover have "Determinant.det ?R = 1"
    using det_four_block_mat_lower_left_zero[OF _ A'_B_dim, of ?IA "?OC" ?ID]
    by (simp add: square)
  ultimately show ?thesis by fastforce
qed

section "Positive Definite Matrix"

definition positive_definite :: "'a::{ord,conjugatable_field} mat ⇒ bool" where
  "positive_definite M ⟷ hermitian M
    ∧ (∀x ∈ carrier_vec (dim_col M). x ≠ 0v (dim_col M) ⟶ QF M x > 0)"

lemma leading_principal_submatrix_positive_definite:
  fixes A :: "'a::{conjugatable_field,ord} mat"
  assumes "A ∈ carrier_mat n n"
  assumes "positive_definite A"
  assumes "k ≤ n"
  shows "positive_definite (lps A k)"
proof-
  define B where "B ≡ lps A k"
  have B_carrier: "B ∈ carrier_mat k k"
    using B_def assms(1) assms(3) leading_principal_submatrix_carrier by blast
  hence B_dims: "dim_row B = k ∧ dim_col B = k" by simp
  { fix v :: "'a vec"
    assume *: "v ∈ carrier_vec k" "v ≠ 0v k"
    define w where "w ≡ vec n (λi. if i < k then v$i else 0)"
    hence "w ≠ 0v n"
      by (smt (verit) "*"(1) "*"(2) assms(3) carrier_vecD dual_order.strict_trans1 eq_vecI index_vec index_zero_vec(1) index_zero_vec(2))
    hence "QF A w > 0" using assms(1) assms(2) positive_definite_def vec_carrier w_def by blast
    moreover have "QF A w = QF B v"
    proof-
      have 1: "⋀i. i ∈ {k..<n} ⟹ conjugate (w$i) = 0" using w_def by simp
      have 2: "⋀i. i ∈ {0..<k} ⟹ w$i = v$i" using assms(3) w_def by auto
      hence 3: "⋀i. i ∈ {0..<k} ⟹ conjugate (w$i) = conjugate (v$i)" by presburger
      have 4: "⋀i. i ∈ {0..<k} ⟹ (A *v w)$i = (B *v v)$i"
      proof-
        fix i assume **: "i ∈ {0..<k}"
        have ***: "⋀j. j ∈ {0..<k} ⟹ (row A i)$j = (row B i)$j"
        proof-
          fix j assume "j ∈ {0..<k}"
          moreover then have "(row A i)$j = A$$(i,j)"
            using "**" * assms(1) assms(3) by force
          ultimately show "(row A i)$j = (row B i)$j"
            by (metis (mono_tags, lifting) ** B_def B_dims assms(1) assms(3) atLeastLessThan_iff index_vec leading_principal_submatrix_index row_def)
        qed
        have ****: "row B i ∈ carrier_vec k ∧ dim_vec v = k"
          using B_carrier B_dims * by auto
        have "(A *v w)$i = row A i ∙ w" using ** assms(1) assms(3) by force
        also have "… = (∑j ∈ {0..<n}. (row A i)$j * w$j)"
          by (metis (no_types, lifting) dim_vec scalar_prod_def sum.cong w_def)
        also have "… = (∑j ∈ {0..<k}. (row A i)$j * w$j) + (∑j ∈ {k..<n}. (row A i)$j * w$j)"
          by (simp add: assms(3) sum.atLeastLessThan_concat)
        also have "… = (∑j ∈ {0..<k}. (row A i)$j * v$j)"
          using 1 2 by auto
        also have "… = (∑j ∈ {0..<k}. (row B i)$j * v$j)"
          using *** by fastforce
        also have "… = (row B i) ∙ v"
          using B_carrier **** unfolding scalar_prod_def by blast
        also have "… = (B *v v)$i" using ** B_carrier by auto
        finally show "(A *v w)$i = (B *v v)$i" .
      qed
      have 5: "v ∈ carrier_vec k" by (simp add: "*"(1))
      hence 6: "B *v v ∈ carrier_vec k ∧ conjugate v ∈ carrier_vec k"
        by (metis B_def Matrix.carrier_vec_conjugate assms(1) assms(3) leading_principal_submatrix_carrier mult_mat_vec_carrier)
      have "QF A w = (A *v w) ∙c w" by simp
      also have "… = (∑i ∈ {0..<n}. (A *v w)$i * conjugate (w$i))"
        by (smt (verit, ccfv_SIG) atLeastLessThan_iff dim_vec dim_vec_conjugate scalar_prod_def sum.cong vec_index_conjugate w_def)
      also have "… = (∑i ∈ {0..<k}. (A *v w)$i * conjugate (w$i))
          + (∑i ∈ {k..<n}. (A *v w)$i * conjugate (w$i))"
        by (simp add: assms(3) sum.atLeastLessThan_concat)
      also have "… = (∑i ∈ {0..<k}. (A *v w)$i * conjugate (w$i))" using 1 by simp
      also have "… = (∑i ∈ {0..<k}. (B *v v)$i * conjugate (v$i))" using 3 4 by force
      also have "… = (B *v v) ∙c v"
        by (smt (verit, best) 5 6 atLeastLessThan_iff carrier_vecD scalar_prod_def sum.cong vec_index_conjugate)
      finally show ?thesis using quadratic_form_def by force
    qed
    ultimately have "QF B v > 0" by argo
  }
  thus ?thesis
    using assms hermitian_ij_ji_iff cmplx_herm_mat.leading_principal_submatrix_hermitian B_dims
    unfolding positive_definite_def B_def
    by (smt (verit, best) carrier_matD(1) leading_principal_submatrix_index order_less_le_trans square_mat.simps)
qed

lemma positive_definite_invertible:
  fixes M :: "complex mat"
  assumes "positive_definite M"
  shows "invertible_mat M"
proof-
  define n where "n ≡ dim_row M"
  have "⋀x. x ∈ carrier_vec n ⟹ x ≠ 0v n ⟹ M *v x ≠ 0v n"
    using assms n_def positive_definite_def hermitian_is_square by force
  hence "mat_kernel M ⊆ {0v n}"
    unfolding mat_kernel_def n_def
    by (metis (mono_tags, lifting) assms hermitian_is_square mem_Collect_eq positive_definite_def singleton_iff square_mat.simps subsetI)
  thus ?thesis
    using trivial_kernel_imp_invertible n_def assms positive_definite_def hermitian_is_square
    by blast
qed

lemma positive_definite_det_nz:
  fixes A :: "complex mat"
  assumes "positive_definite A"
  shows "Determinant.det A ≠ 0"
  using positive_definite_invertible[OF assms] invertible_det invertible_mat_def square_mat.simps
  by blast

section "Matrix-Vector Multiplication"

(* This lemma is copy/pasted from Generalize Linear_Algebra_Complements.diagonal_unit_vec
   with only the type relaxed from complex to 'a::{ring,zero_neq_one} *)
lemma diagonal_unit_vec':
  assumes "B ∈ carrier_mat n n"
  assumes "diagonal_mat (B::'a::{ring,zero_neq_one} Matrix.mat)"
  shows "B *v (unit_vec n i) = B $$ (i,i)  ⋅v (unit_vec n i)"
proof -
  define v :: "'a Matrix.vec" where "v = unit_vec n i"
  have "B *v v = Matrix.vec n (λ i. Matrix.scalar_prod (Matrix.row B i) v)" 
    using assms unfolding mult_mat_vec_def by simp
  also have "… = Matrix.vec n (λ i. B $$(i,i) * Matrix.vec_index v i)" 
  proof -
    have "∀i < n. (Matrix.scalar_prod (Matrix.row B i) v = B $$(i,i) * Matrix.vec_index v i)"
    proof (intro allI impI)
      fix i
      assume "i < n"
      have "(Matrix.scalar_prod (Matrix.row B i) v) = 
        (∑ j ∈ {0 ..< n}. Matrix.vec_index (Matrix.row B i) j * Matrix.vec_index v j)" using assms
        unfolding Matrix.scalar_prod_def v_def by simp
      also have "… = Matrix.vec_index (Matrix.row B i) i * Matrix.vec_index v i" 
      proof (rule sum_but_one)
        show "∀j < n. j ≠ i ⟶ Matrix.vec_index (Matrix.row B i) j = 0"
        proof (intro allI impI)
          fix j
          assume "j < n" and "j ≠ i"
          hence "Matrix.vec_index (Matrix.row B i) j = B $$ (i,j)" using ‹i < n› ‹j < n› assms 
            by auto
          also have "… = 0" using assms ‹i < n› ‹j < n› ‹j≠ i› unfolding diagonal_mat_def by simp
          finally show "Matrix.vec_index (Matrix.row B i) j = 0" .
        qed
        show "i < n" using ‹i < n› .
      qed
      also have "… = B $$(i,i) * Matrix.vec_index v i" using assms ‹i < n› by auto
      finally show "(Matrix.scalar_prod (Matrix.row B i) v) = B $$(i,i) * Matrix.vec_index v i" .
    qed
    thus ?thesis by auto
  qed
  also have "... = B $$ (i,i)  ⋅v v" unfolding v_def unit_vec_def by auto
  finally have "B *v v = B $$ (i,i)  ⋅v v" .
  thus ?thesis unfolding v_def by simp
qed

lemma mat_vec_idx_disjunct_eq_zero:
  assumes A: "A ∈ carrier_mat m n"
  assumes x: "x ∈ carrier_vec n"
  assumes some_zero: "∀j < n. (row A i)$j = 0 ∨ x$j = 0"
  assumes i: "i < m"
  shows "(A *v x)$i = 0"
proof-
  have dim_x: "dim_vec x = n" using x by simp
  have dim_A: "dim_row A = m" "dim_col A = n" using A by blast+
  have all_zero: "∀j < n. (row A i)$j * x$j = 0" using some_zero by fastforce

  have "(A *v x)$i = (row A i) ∙ x" by (rule index_mult_mat_vec, subst dim_A(1), rule i)
  also have "… = (∑j < n. (row A i)$j * x$j)"
    unfolding scalar_prod_def dim_x using atLeast0LessThan by presburger
  also have "… = 0" using all_zero by simp
  finally show ?thesis .
qed

section "Module Span"

context module
begin

lemma mk_coeffs_of_list:
  assumes "α ∈ (set A → carrier R)"
  shows "∃c ∈ {0..<length A} → carrier R. ∀v ∈ set A. mk_coeff A c v = α v"
  using assms
proof(induct "length A" arbitrary: A)
  case 0
  thus ?case by fastforce
next
  case (Suc n)
  then obtain a A' where a: "A = A' @ [a]" by (metis length_Suc_conv_rev)
  hence "α ∈ (set A' → carrier R)" using Suc.prems by simp
  moreover from a have len_A': "n = length A'" using Suc(2) by auto
  ultimately obtain c' where c':
      "c' ∈ {0..<length A'} → carrier R ∧ (∀v ∈ set A'. mk_coeff A' c' v = α v)"
    using Suc.hyps by blast

  have len_A'_A: "length A' = length A - 1" using Suc(2) len_A' by presburger
  moreover have "[0..<length A] = [0..<length A - 1] @ [length A - 1]"
    by (metis Suc(2) calculation len_A' upt_Suc_append zero_order(1))
  ultimately have A_A'_int: "[0..<length A] = [0..<length A'] @ [length A']" by presburger
  show ?case
  proof(cases "a ∈ set A'")
    case True
    hence A_A': "set A = set A'" using a by auto
    define c where "c ≡ (λi. if i ∈ {0..<length A'} then c' i else 𝟬)"
    hence c_carrier: "c ∈ {0..<length A} → carrier R" using c' by force
    moreover have "∀v ∈ set A. mk_coeff A c v = α v"
    proof
      fix v assume *: "v ∈ set A"
      show "mk_coeff A c v = α v"
      proof(cases "v = a")
        case True
        hence "find_indices v A = (find_indices v A') @ [length A - 1]" 
        proof-
          from A_A'_int have "find_indices v A
              = (filter (λi. A ! i = v) [0..<length A']) @ (filter (λi. A ! i = v) [length A'])"
            unfolding find_indices_def by (metis Suc.hyps(2) filter_append len_A')
          moreover then have "(filter (λi. A ! i = v) [0..<length A']) = (find_indices v A')"
            using a by auto
          moreover have "filter (λi. A ! i = v) [length A'] = [length A']" by (simp add: True a)
          ultimately show ?thesis using len_A'_A by argo
        qed
        hence "map c (find_indices v A) = (map c (find_indices v A')) @ [c (length A - 1)]"
          by auto
        hence "foldr (⊕) (map c (find_indices v A)) 𝟬
            = foldr (⊕) (map c (find_indices v A')) (𝟬 ⊕ c (length A - 1))"
          by (simp add: c_def len_A'_A)
        also have "… = (foldr (⊕) (map c (find_indices v A')) 𝟬) ⊕ c (length A - 1)"
          by (metis R.sumlist_def R.zero_closed c_carrier atLeastLessThan_iff c_def calculation cring_simprules(16) len_A'_A less_irrefl_nat mk_coeff_carrier mk_coeff_def)
        finally have "mk_coeff A c v = mk_coeff A' c v ⊕ c (length A - 1)"
          unfolding mk_coeff_def R.sumlist_def .
        moreover have "c (length A - 1) = 𝟬" unfolding c_def using len_A' a by force
        moreover have "mk_coeff A' c v ∈ carrier R"
          by (smt (verit) Pi_iff c' c_def mk_coeff_carrier)
        ultimately have "mk_coeff A c v = mk_coeff A' c v" by algebra
        moreover have "mk_coeff A' c v = mk_coeff A' c' v"
          unfolding mk_coeff_def find_indices_def
          by (metis (mono_tags, lifting) c_def list.map_cong mem_Collect_eq set_filter set_upt)
        ultimately show ?thesis by (metis "*" A_A' c')
      next
        case v_neq_a: False
        hence "find_indices v A = find_indices v A'"
        proof-
          from A_A'_int have "find_indices v A
              = (filter (λi. A ! i = v) [0..<length A']) @ (filter (λi. A ! i = v) [length A'])"
            unfolding find_indices_def by (metis Suc.hyps(2) filter_append len_A')
          moreover have "(filter (λi. A ! i = v) [0..<length A']) = find_indices v A'"
            unfolding find_indices_def
            by (smt (verit, ccfv_SIG) a append.right_neutral calculation filter.simps(1) filter.simps(2) filter_cong find_indices_def find_indices_snoc nth_append_length v_neq_a)
          moreover have "(filter (λi. A ! i = v) [length A']) = []" using a v_neq_a by auto
          ultimately show ?thesis by force
        qed
        hence "mk_coeff A c v = mk_coeff A' c v" unfolding mk_coeff_def by fastforce
        also have "… = mk_coeff A' c' v"
          unfolding mk_coeff_def find_indices_def c_def
          by (smt (verit, best) atLeastLessThan_upt list.map_cong mem_Collect_eq set_filter)
        also have "… = α v" using c' * A_A' by blast
        finally show ?thesis .
      qed
    qed
    ultimately show ?thesis by blast
  next
    case False
    hence A_A': "set A' = set A - {a}" by (simp add: a)
    define c where "c ≡ (λi. if i ∈ {0..<length A'} then c' i else α a)"
    hence "c ∈ {0..<length A} → carrier R" using Suc.prems a c' by fastforce
    moreover have "∀v ∈ set A. mk_coeff A c v = α v"
    proof
      fix v assume *: "v ∈ set A"
      show "mk_coeff A c v = α v"
      proof(cases "v = a")
        case v_eq_a: True
        hence "filter (λi. A ! i = v) [0..<length A] = [length A - 1]"
        proof-
          from A_A'_int have "filter (λi. A ! i = v) [0..<length A]
              = (filter (λi. A ! i = v) [0..<length A']) @ (filter (λi. A ! i = v) [length A'])"
            by (metis Suc.hyps(2) filter_append len_A')
          moreover have "(filter (λi. A ! i = v) [0..<length A']) = []"
          proof-
            have "⋀i. i < length A' ⟹ A!i ≠ v" by (metis False a nth_append nth_mem v_eq_a)
            thus ?thesis by fastforce
          qed
          moreover have "(filter (λi. A ! i = v) [length A']) = [length A']" by (simp add: a v_eq_a)
          ultimately show ?thesis by (metis Suc.hyps(2) diff_Suc_1 len_A' self_append_conv2)
        qed
        hence "map c (filter (λi. A ! i = v) [0..<length A]) = [c (length A')]"
          by (metis len_A' Suc.hyps(2) diff_Suc_1 list.map(1) list.map(2))
        moreover have "c (length A') = α v" by (simp add: v_eq_a c_def)
        ultimately have "mk_coeff A c v = α v ⊕ 𝟬" unfolding mk_coeff_def find_indices_def by force
        moreover have "α v ∈ carrier R" using "*" Suc(3) by blast
        ultimately show ?thesis by auto
      next (* copied exactly from the other v_neq_a case *)
        case v_neq_a: False
        hence "find_indices v A = find_indices v A'"
        proof-
          from A_A'_int have "find_indices v A
              = (filter (λi. A ! i = v) [0..<length A']) @ (filter (λi. A ! i = v) [length A'])"
            unfolding find_indices_def by (metis Suc.hyps(2) filter_append len_A')
          moreover have "(filter (λi. A ! i = v) [0..<length A']) = find_indices v A'"
            unfolding find_indices_def
            by (smt (verit, ccfv_SIG) a append.right_neutral calculation filter.simps(1) filter.simps(2) filter_cong find_indices_def find_indices_snoc nth_append_length v_neq_a)
          moreover have "(filter (λi. A ! i = v) [length A']) = []" using a v_neq_a by auto
          ultimately show ?thesis by force
        qed
        hence "mk_coeff A c v = mk_coeff A' c v" unfolding mk_coeff_def by fastforce
        also have "… = mk_coeff A' c' v"
          unfolding mk_coeff_def find_indices_def c_def
          by (smt (verit, best) atLeastLessThan_upt list.map_cong mem_Collect_eq set_filter)
        also have "… = α v" by (metis c' * a insert_iff v_neq_a vec_space.append_insert)
        finally show ?thesis .
      qed
    qed
    ultimately show ?thesis by blast
  qed
qed

lemma span_list_span:
  assumes "set A ⊆ carrier M"
  shows "span_list A = span (set A)"
proof-
  have "span_list A ⊆ span (set A)"
  proof(rule subsetI)
    fix x assume "x ∈ span_list A"
    then obtain c where c: "x = lincomb_list c A ∧ c ∈ {0..<length A} → carrier R"
      unfolding span_list_def by blast
    hence 1: "lincomb_list c A = lincomb (mk_coeff A c) (set A)"
      using lincomb_list_as_lincomb[OF assms(1)] by presburger
    have "mk_coeff A c ∈ (set A) → carrier R" by (simp add: c mk_coeff_carrier)
    hence 2: "lincomb (mk_coeff A c) (set A) ∈ span (set A)" unfolding span_def by blast
    show "x ∈ span (set A)" using 1 2 c by argo
  qed
  moreover have "span (set A) ⊆ span_list A"
  proof(rule subsetI)
    fix x assume *: "x ∈ span (set A)"
    then obtain α where α: "x = lincomb α (set A) ∧ α ∈ (set A → carrier R)"
      using * finite_span assms by auto
    define α' where "α' = (λv. if v ∈ set A then α v else 𝟬)"
    hence α_α': "⋀v. v ∈ set A ⟹ α v = α' v" by presburger
    hence x_α': "x = lincomb α' (set A)"
      using α
      unfolding lincomb_def
      by (smt (verit) M.add.finprod_cong' Pi_iff assms basic_trans_rules(31) carrier_is_submodule submoduleE(4))
    have 1: "α' ∈ (set A → carrier R)" by (simp add: Pi_cong α α'_def)
    then obtain c where c: "c ∈ {0..<length A} → carrier R ∧ (∀v ∈ set A. mk_coeff A c v = α' v)"
      using mk_coeffs_of_list by blast
    have "mk_coeff A c = α'" unfolding α'_def by (metis mk_coeff_0 c α_α')
    hence "lincomb α' (set A) = lincomb_list c A"
        using lincomb_list_as_lincomb[OF assms(1), of c] c by argo
    also have "… ∈ span_list A" using c in_span_listI by blast
    finally show "x ∈ span_list A" using x_α' by blast
  qed
  ultimately show ?thesis by blast
qed

end

section "Module Homomorphism, Linear Combination, and Span"

context mod_hom
begin

lemma lincomb_list_distrib:
  assumes "set S ⊆ carrier M"
  assumes "α ∈ {..<length S} → carrier R"
  shows "f (M.lincomb_list α S) = N.lincomb_list α (map f S)"
  using assms
proof(induct "length S" arbitrary: S α)
  case 0
  then show ?case by auto
next
  case (Suc n)
  then obtain v S' where v: "S = v # S'" by (metis length_Suc_conv)
  have 1: "n = length S'" using Suc(2) v by auto
  have 2: "set S' ⊆ carrier M" using Suc(3) v by auto
  have 3: "(α ∘ Suc) ∈ {..<length S'} → carrier R" using "1" Suc(4) Suc.hyps(2) by fastforce
  
  have ih: "f (M.lincomb_list (α ∘ Suc) S') = N.lincomb_list (α ∘ Suc) (map f S')"
    using Suc.hyps(1)[OF 1 2 3] .
  
  have *: "M.lincomb_list α (v # S') = (α 0 ⊙⇘M⇙ v) ⊕⇘M⇙ (M.lincomb_list (α ∘ Suc) S')"
    using M.lincomb_list_Cons .
  have "v ∈ carrier M" using Suc.prems(1) v by force
  moreover have "α 0 ∈ carrier R" using Suc(4) Suc.hyps(2) by auto
  ultimately have "α 0 ⊙⇘M⇙ v ∈ carrier M" by blast
  moreover have "M.lincomb_list (α ∘ Suc) S' ∈ carrier M"
    by (metis (no_types, lifting) "1" "2" M.lincomb_list_carrier Pi_iff Suc(4) Suc.hyps(2) Suc_less_eq atLeastLessThan_iff lessThan_iff o_apply)
  ultimately have "f (M.lincomb_list α S) = (f (α 0 ⊙⇘M⇙ v)) ⊕⇘N⇙ (f (M.lincomb_list (α ∘ Suc) S'))"
    using f_hom * v unfolding module_hom_def by force
  also have "… = (α 0 ⊙⇘N⇙ f v) ⊕⇘N⇙ (f (M.lincomb_list (α ∘ Suc) S'))"
    by (simp add: ‹α 0 ∈ carrier R› ‹v ∈ carrier M›)
  also have "… = (α 0 ⊙⇘N⇙ f v) ⊕⇘N⇙ (N.lincomb_list (α ∘ Suc) (map f S'))" using ih by argo
  also have "… = N.lincomb_list α (map f S)"
    using N.lincomb_list_Cons by (simp add: v)
  finally show ?case .
qed

lemma lincomb_distrib:
  assumes "inj_on f S"
  assumes "S ⊆ carrier M"
  assumes "α ∈ S → carrier R"
  assumes "∀v ∈ S. α v = β (f v)"
  assumes "finite S"
  shows "f (M.lincomb α S) = N.lincomb β (f`S)"
proof-
  let ?α' = "(λv. (α v) ⊙⇘M⇙ v)"
  let ?β' = "(λv. (β v) ⊙⇘N⇙ v)"
  have *: "?α' ∈ S → carrier M" using assms(2,3) by auto
  have "f (M.lincomb α S) = f (finsum M ?α' S)" using M.lincomb_def by presburger
  also have "… = (⨁⇘N⇙a∈S. f ((α a) ⊙⇘M⇙ a))" using hom_sum[OF assms(2) *] .
  also have "… = (⨁⇘N⇙a∈S. (α a) ⊙⇘N⇙ (f a))"
  proof-
    have "∀a ∈ S. a ∈ carrier M" using assms(2) by fastforce
    moreover have "∀a ∈ S. α a ∈ carrier R" using assms(3) by blast
    ultimately show ?thesis
      using f_hom unfolding module_hom_def by (simp add: N.M.add.finprod_cong')
  qed
  also have "… = (⨁⇘N⇙a∈S. (β (f a)) ⊙⇘N⇙ (f a))"
    by (smt (verit, del_insts) N.M.add.finprod_cong' M.summands_valid PiE Pi_I assms(2-4) basic_trans_rules(31) f_im f_smult)
  also have "… = (⨁⇘N⇙a∈(f`S). (?β' a))"
    by (smt (verit, best) assms(1,4) M.summands_valid N.M.add.finprod_cong' N.M.add.finprod_reindex PiE Pi_I assms(2) assms(3) assms(4) basic_trans_rules(31) f_im f_smult imageE)
  also have "… = N.lincomb β (f`S)" using N.lincomb_def by presburger
  finally show ?thesis .
qed

lemma lincomb_distrib_obtain:
  assumes "inj_on f S"
  assumes "S ⊆ carrier M"
  assumes "α ∈ S → carrier R"
  assumes "∀v ∈ S. α v = β (f v)"
  assumes "finite S"
  obtains β where "(∀v ∈ S. α v = β (f v)) ∧ f (M.lincomb α S) = N.lincomb β (f`S)"
proof-
  obtain β where β: "∀v ∈ S. α v = β (f v)"
  proof-
    let ?β = "λy. α (THE x. x ∈ S ∧ f x = y)"
    have "∀v ∈ S. α v = ?β (f v)"
    proof
      fix v assume "v ∈ S"
      then have "v = (THE x. x ∈ S ∧ f x = f v)"
        using assms(1) by (simp add: inj_on_def the_equality)
      thus "α v = ?β (f v)" by argo
    qed
    thus ?thesis using that by fast
  qed
  thus ?thesis using lincomb_distrib that assms by blast
qed

lemma image_span_list:
  assumes "set vs ⊆ carrier M"
  shows "f`(M.span_list vs) = N.span_list (map f vs)" (is "?lhs = ?rhs")
proof-
  have "?lhs ⊆ ?rhs"
  proof(rule subsetI)
    fix w assume "w ∈ ?lhs"
    then obtain v where v: "v ∈ M.span_list vs ∧ f v = w" by blast
    then obtain α where α: "v = M.lincomb_list α vs ∧ α ∈ {..<length vs} → carrier R"
      unfolding M.span_list_def by fastforce
    hence "f v = N.lincomb_list α (map f vs)" using lincomb_list_distrib[OF assms(1)] v by blast
    thus "w ∈ ?rhs" using v α unfolding N.span_list_def by force
  qed
  moreover have "?rhs ⊆ ?lhs"
  proof(rule subsetI)
    fix w assume "w ∈ ?rhs"
    then obtain α where α: "w = N.lincomb_list α (map f vs) ∧ α ∈ {..<length vs} → carrier R"
      unfolding N.span_list_def by fastforce
    hence "w = f (M.lincomb_list α vs)"
      using lincomb_list_distrib[OF assms] by presburger
    thus "w ∈ ?lhs" using α unfolding M.span_list_def by fastforce
  qed
  ultimately show ?thesis by blast
qed

lemma image_span:
  assumes "finite vs"
  assumes "vs ⊆ carrier M"
  shows "f`(M.span vs) = N.span (f`vs)"
proof-
  obtain vs_list where vs_list: "set vs_list = vs" using assms(1) finite_list by blast
  have "f`vs = set (map f vs_list)" using vs_list by simp
  hence "N.span (f`vs) = N.span_list (map f vs_list)"
    by (metis N.span_list_span M.sum_simp assms(2) f_im image_subset_iff)
  moreover have "M.span vs = M.span_list vs_list"
    using M.span_list_span vs_list assms(2) by presburger
  ultimately show ?thesis using image_span_list assms vs_list by presburger
qed

lemma submodule_image:
  assumes "submodule R M' M"
  shows "submodule R (f`M') N"
  using assms
  by (smt (verit, ccfv_threshold) submodule_def N.module_axioms f0_is_0 f_add f_im f_smult
      imageE rev_image_eqI subset_iff)

lemma submodule_restrict:
  assumes "submodule R M' M"
  shows "mod_hom R (M.md M') (N.md (f`M')) f"
proof-
  interpret M': module R "M.md M'" by (simp add: M.submodule_is_module assms)
  interpret N': module R "N.md (f`M')" by (rule submodule_is_module, rule submodule_image[OF assms])
  have "f ∈ module_hom R (M.md M') (N.md (f`M'))"
    by (simp add: module_hom_def, meson M'.sum_simp assms f_add f_smult submodule.subset)
  thus ?thesis
    apply (simp add: mod_hom_def mod_hom_axioms_def)
    using M'.module_axioms N'.module_axioms by blast
qed

end

section "Vector Summation"

lemma complex_vec_norm_sum:
  fixes x :: "complex vec"
  assumes "x ∈ carrier_vec n"
  shows "vec_norm x = csqrt ((∑i ∈ {..<n}. (cmod (x$i))^2))"
proof-
  have *: "⋀i. i ∈ {..<n} ⟹ (conjugate x)$i = conjugate (x$i)"
    using assms by auto
  have **: "⋀i. i ∈ {..<n} ⟹ (x$i) * conjugate (x$i) = (cmod (x$i))^2"
    using mult_conj_cmod_square by blast
  have "vec_norm x = csqrt (x ∙c x)"
    by (simp add: vec_norm_def)
  also have "… = csqrt (∑i ∈ {..<n}. (x$i) * (conjugate x)$i)"
    by (metis assms atLeast0LessThan carrier_vecD dim_vec_conjugate scalar_prod_def)
  also have "… = csqrt (∑i ∈ {..<n}. (x$i) * conjugate (x$i))"
    by (simp add: *)
  also have "… = csqrt ((∑i ∈ {..<n}. (cmod (x$i))^2))" using ** by fastforce
  finally show ?thesis .
qed

lemma inner_prod_vec_sum:
  assumes "v ∈ carrier_vec n"
  assumes "w ∈ carrier_vec n"
  assumes "B ⊆ carrier_vec n"
  assumes "finite B"
  assumes "v = finsum_vec TYPE('a::conjugatable_ring) n (λb. cs b ⋅v b) B"
  shows "inner_prod w v = (∑b ∈ B. cs b * inner_prod w b)"
proof-
  let ?vs = "λb. cs b ⋅v b"
  let ?f = "λi b. if i ∈ {..<n} then (?vs b)$i * (conjugate w)$i else 0"
  have f: "⋀y. finite {x. ?f x y ≠ 0}"
  proof-
    fix y
    have "{x. ?f x y ≠ 0} ⊆ {..<n}" by (simp add: subset_eq)
    thus "finite {x. ?f x y ≠ 0}" using finite_nat_iff_bounded by blast
  qed
  have vs: "?vs ∈ B → carrier_vec n" using assms(3) by force
  have b_scale: "⋀i b. i ∈ {..<n} ⟹ b ∈ B ⟹ (?vs b)$i = cs b * b$i"
    using assms(3) by auto
  have assoc: "⋀i b. (cs b * b$i) * (conjugate w)$i = cs b * (b$i * (conjugate w)$i)"
    using Groups.mult_ac(1) by blast

  have "inner_prod w v = (∑i ∈ {..<n}. v$i * (conjugate w)$i)"
    unfolding scalar_prod_def using atLeast0LessThan assms(2) by force
  moreover have "⋀i. i ∈ {..<n} ⟹ v$i = (∑b ∈ B. (?vs b)$i)"
    using index_finsum_vec[OF assms(4) _ vs] unfolding assms(5) by blast
  ultimately have "inner_prod w v = (∑i ∈ {..<n}. (∑b ∈ B. (?vs b)$i) * (conjugate w)$i)"
    by force
  also have "… = (∑i ∈ {..<n}. ∑b ∈ B. (?vs b)$i * (conjugate w)$i)"
    by (simp add: sum_distrib_right)
  also have "… = (∑i ∈ {..<n}. ∑b ∈ B. ?f i b)"
    by fastforce
  also have "… = Sum_any (λi. ∑b ∈ B. ?f i b)"
    using Sum_any.conditionalize[of "{..<n}" "λi. (∑b ∈ B. ?f i b)"]
    by (smt (verit, ccfv_SIG) Sum_any.cong finite_nat_iff_bounded subset_eq sum.neutral)
  also have "… = (∑b ∈ B. (Sum_any (λi. ?f i b)))"
    using Sum_any_sum_swap[OF assms(4) f, of "λx. x"] .
  also have "… = (∑b ∈ B. (∑i ∈ {..<n}. (?vs b)$i * (conjugate w)$i))"
  proof-
    have "⋀b. b ∈ B ⟹ (∑i ∈ {..<n}. (?vs b)$i * (conjugate w)$i) = Sum_any (λi. ?f i b)"
      using Sum_any.conditionalize[of "{..<n}"] by blast
    thus ?thesis by fastforce
  qed
  also have "… = (∑b ∈ B. (∑i ∈ {..<n}. (cs b * b$i) * (conjugate w)$i))"
    using b_scale by simp
  also have "… = (∑b ∈ B. (∑i ∈ {..<n}. cs b * (b$i * (conjugate w)$i)))"
    using assoc by force
  also have "… = (∑b ∈ B. cs b * (∑i ∈ {..<n}. b$i * (conjugate w)$i))"
    by (simp add: sum_distrib_left)
  also have "… = (∑b ∈ B. cs b * inner_prod w b)"
    unfolding scalar_prod_def using atLeast0LessThan assms(2) by force
  finally show ?thesis  .
qed

lemma sprod_vec_sum:
  assumes "v ∈ carrier_vec n"
  assumes "w ∈ carrier_vec n"
  assumes "B ⊆ carrier_vec n"
  assumes "finite B"
  assumes "v = finsum_vec TYPE('a::{comm_ring}) n (λb. cs b ⋅v b) B"
  shows "w ∙ v = (∑b ∈ B. cs b * (w ∙ b))"
proof-
  let ?vs = "λb. cs b ⋅v b"
  let ?f = "λi b. if i ∈ {..<n} then (?vs b)$i * w$i else 0"
  have f: "⋀y. finite {x. ?f x y ≠ 0}"
  proof-
    fix y
    have "{x. ?f x y ≠ 0} ⊆ {..<n}" by (simp add: subset_eq)
    thus "finite {x. ?f x y ≠ 0}" using finite_nat_iff_bounded by blast
  qed

  have vs: "?vs ∈ B → carrier_vec n" using assms(3) by force
  have b_scale: "⋀i b. i ∈ {..<n} ⟹ b ∈ B ⟹ (?vs b)$i = cs b * b$i"
    using assms(3) by auto
  have assoc: "⋀i b. (cs b * b$i) * w$i = cs b * (b$i * w$i)"
    using Groups.mult_ac(1) by blast
  have B_dim: "⋀b. b ∈ B ⟹ dim_vec b = n"
    using assms(3) by fastforce

  have "w ∙ v = (∑i ∈ {..<n}. v$i * w$i)"
    unfolding scalar_prod_def using atLeast0LessThan[of n] assms(1)
    by (metis (no_types, lifting) Groups.mult_ac(2) carrier_vecD sum.cong)
  moreover have "⋀i. i ∈ {..<n} ⟹ v$i = (∑b ∈ B. (?vs b)$i)"
    using index_finsum_vec[OF assms(4) _ vs] unfolding assms(5) by blast
  ultimately have "w ∙ v = (∑i ∈ {..<n}. (∑b ∈ B. (?vs b)$i) * w$i)"
    by force
  also have "… = (∑i ∈ {..<n}. ∑b ∈ B. (?vs b)$i * w$i)"
    by (simp add: sum_distrib_right)
  also have "… = (∑i ∈ {..<n}. ∑b ∈ B. ?f i b)"
    by fastforce
  also have "… = Sum_any (λi. ∑b ∈ B. ?f i b)"
    using Sum_any.conditionalize[of "{..<n}" "λi. (∑b ∈ B. ?f i b)"]
    by (smt (verit, ccfv_SIG) Sum_any.cong finite_nat_iff_bounded subset_eq sum.neutral)
  also have "… = (∑b ∈ B. (Sum_any (λi. ?f i b)))"
    using Sum_any_sum_swap[OF assms(4) f, of "λx. x"] .
  also have "… = (∑b ∈ B. (∑i ∈ {..<n}. (?vs b)$i * w$i))"
  proof-
    have "⋀b. b ∈ B ⟹ (∑i ∈ {..<n}. (?vs b)$i * w$i) = Sum_any (λi. ?f i b)"
      using Sum_any.conditionalize[of "{..<n}"] by blast
    thus ?thesis by fastforce
  qed
  also have "… = (∑b ∈ B. (∑i ∈ {..<n}. (cs b * b$i) * w$i))"
    using b_scale by simp
  also have "… = (∑b ∈ B. (∑i ∈ {..<n}. cs b * (b$i * w$i)))"
    using assoc by force
  also have "… = (∑b ∈ B. cs b * (∑i ∈ {..<n}. b$i * w$i))"
    by (simp add: sum_distrib_left)
  also have "… = (∑b ∈ B. cs b * (∑i ∈ {..<n}. w$i * b$i))"
    by (metis (no_types, lifting) Groups.mult_ac(2) sum.cong)
  also have "… = (∑b ∈ B. cs b * (w ∙ b))"
    unfolding scalar_prod_def using atLeast0LessThan assms(3) B_dim by fastforce
  finally show ?thesis  .
qed

lemma mat_vec_mult_sum:
  assumes "v ∈ carrier_vec n"
  assumes "A ∈ carrier_mat n n"
  assumes "B ⊆ carrier_vec n"
  assumes "finite B"
  assumes "v = finsum_vec TYPE('a::comm_ring) n (λb. cs b ⋅v b) B"
  shows "A *v v = finsum_vec TYPE('a::comm_ring) n (λb. cs b ⋅v (A *v b)) B"
    (is "?lhs = ?rhs")
proof-
  have "⋀i. i < n ⟹ ?lhs$i = ?rhs$i"
  proof-
    fix i assume *: "i < n"
    let ?r = "row A i"
    have "?lhs$i = ?r ∙ v" using * assms(2) unfolding mult_mat_vec_def by simp
    also have "… = (∑b ∈ B. (cs b * (?r ∙ b)))"
      using sprod_vec_sum[OF assms(1) _ assms(3) assms(4) assms(5)] assms(2) by fastforce
    also have "… = (∑b ∈ B. (cs b * ((A *v b)$i)))"
      using * assms(2) unfolding mult_mat_vec_def by simp
    also have "… = (∑b ∈ B. ((cs b ⋅v (A *v b))$i))"
      using assms(2) * by force
    also have "… = (finsum_vec TYPE('a::comm_ring) n (λb. cs b ⋅v (A *v b)) B)$i"
      using index_finsum_vec[OF assms(4) *]
      by (smt (verit, best) Pi_I assms(2) carrier_matD(1) carrier_vec_dim_vec dim_mult_mat_vec index_smult_vec(2) sum.cong)
    finally show "?lhs$i = ?rhs$i" by blast
  qed
  moreover have "?lhs ∈ carrier_vec n" using assms(1) assms(2) by force
  moreover have "?rhs ∈ carrier_vec n"
    by (smt (verit, ccfv_SIG) Pi_iff assms(2) assms(3) finsum_vec_closed mult_mat_vec_carrier smult_carrier_vec subsetD)
  ultimately show ?thesis by (metis (no_types, lifting) carrier_vecD eq_vecI)
qed

section "Vector Space"

context vectorspace
begin

lemma basis_subset_carrier: "basis B ⟹ B ⊆ carrier V" by (simp add: basis_def)

lemma dim_of_lin_indpt_span:
  assumes li: "lin_indpt S"
  assumes carrier: "S ⊆ carrier V"
  assumes fin: "finite S"
  shows "vectorspace.dim K (vs (span S)) = card S"
  using dim_span[unfolded maximal_def, OF carrier fin] li
  by fast

lemma subspace_fin_dim:
  assumes W: "subspace K W V"
  assumes fin: "fin_dim"
  shows "vectorspace.fin_dim K (vs W)"
proof-
  have ?thesis if "{𝟬⇘V⇙} = W"
    using W that
    by (metis empty_subsetI finite.emptyI local.carrier_vs_is_self local.span_empty span_li_not_depend(1) subspace.submod
        subspace_is_vs vectorspace.fin_dim_def)
  moreover have ?thesis if *: "{𝟬⇘V⇙} ≠ W"
  proof-
    obtain b where b: "b ∈ W ∧ b ≠ 𝟬⇘V⇙" using W * submodule_def subspace_def by blast
    let ?P = "λB. B ⊆ W ∧ lin_indpt B"
    have "⋀B. ?P B ⟹ finite B ∧ card B ≤ dim"
      using W fin by (metis submodule_def subspace_def dual_order.trans li_le_dim(1,2))
    moreover have Pb: "?P {b}"
    proof-
      have "b ∉ span {}" by (simp add: b local.span_empty)
      moreover have "{b} ⊆ carrier V" using b W[unfolded subspace_def submodule_def] by fast
      ultimately show ?thesis using lindep_span[of "{b}", simplified] b by blast
    qed
    ultimately obtain B where B: "finite B ∧ maximal B ?P" by (metis (no_types, lifting) maximal_exists)

    have "(λS. S ⊆ W ∧ ¬ LinearCombinations.module.lin_dep K (vs W) S) = ?P"
      using B[unfolded maximal_def] span_li_not_depend(2) W by auto
    hence "vectorspace.basis K (vs W) B"
      using B vectorspace.max_li_is_basis[OF subspace_is_vs[OF W], of B, simplified] by argo
    thus ?thesis
      using B[unfolded maximal_def]
      unfolding vectorspace.fin_dim_def[OF subspace_is_vs[OF W]] vectorspace.basis_def[OF subspace_is_vs[OF W]]
      by auto
  qed
  ultimately show ?thesis by argo
qed

lemma nontriv_obtain:
  assumes "dim ≠ 0" "fin_dim"
  obtains v where "v ∈ carrier V" "v ≠ 𝟬⇘V⇙"
proof-
  obtain B where B: "basis B ∧ 0 < card B" using assms dim_basis finite_basis_exists by force
  then obtain b where "b ∈ B ∧ b ≠ 𝟬⇘V⇙"
    by (metis all_not_in_conv basis_def bot_nat_0.not_eq_extremum card_eq_0_iff vs_zero_lin_dep)
  thus ?thesis using that[of b] B[unfolded basis_def] by blast
qed                                          

lemma nontriv_subspace_obtain:
  assumes "subspace K W V"
  assumes "vectorspace.dim K (vs W) ≠ 0"
  assumes "fin_dim"
  obtains v where "v ∈ W" "v ≠ 𝟬⇘V⇙"
  using assms vectorspace.nontriv_obtain[of K "vs W"] subspace_fin_dim subspace_is_vs by auto

lemma nontriv_exists:
  assumes "dim ≠ 0" "fin_dim"
  shows "∃v ∈ carrier V. v ≠ 𝟬⇘V⇙"
  using nontriv_obtain[OF assms] by metis

lemma nontriv_subspace_exists:
  assumes "subspace K W V"
  assumes "vectorspace.dim K (vs W) ≠ 0"
  assumes "fin_dim"
  shows "∃v ∈ W. v ≠ 𝟬⇘V⇙"
  using nontriv_subspace_obtain[OF assms] by metis

lemma dim_sum_nontriv_int:
  assumes W1: "subspace K W1 V"
  assumes W2: "subspace K W2 V"
  assumes dim: "dim < vectorspace.dim K (vs W1) + vectorspace.dim K (vs W2)"
  assumes fin: "fin_dim"
  shows "∃v ∈ W1 ∩ W2. v ≠ 𝟬⇘V⇙"
proof-
  obtain B1 where B1: "vectorspace.basis K (vs W1) B1" "finite B1"
    using W1 fin subspace_fin_dim subspace_is_vs vectorspace.finite_basis_exists by blast
  obtain B2 where B2: "vectorspace.basis K (vs W2) B2" "finite B2"
    using W2 fin subspace_fin_dim subspace_is_vs vectorspace.finite_basis_exists by blast

  have basis_carriers: "B1 ⊆ carrier V" "B2 ⊆ carrier V"
    using B1 W1 unfolding vectorspace.basis_def[OF subspace_is_vs[OF W1]] subspace_def submodule_def
    using B2 W2 unfolding vectorspace.basis_def[OF subspace_is_vs[OF W2]] subspace_def submodule_def
    by auto

  show ?thesis
  proof(cases "B1 ∩ B2 ≠ {}")
    case True
    then obtain b where "b ∈ B1 ∩ B2" by blast
    hence "b ∈ W1 ∩ W2 ∧ b ≠ 𝟬⇘V⇙"
      using B1 B2 W1 W2
      using basic_trans_rules(31)[of B1 W1 b] basic_trans_rules(31)[of B2 W2 b] one_zeroI
        span_li_not_depend(2)[of B1 W1] subspace_is_vs[of W2] subspace_is_vs[of W1] vectorspace.basis_def[of K "vs W2" B2]
        vectorspace.basis_def[of K "vs W1" B1] zero_lin_dep[of B1]
      by force
    thus ?thesis by blast
  next
    case False
    hence card: "card (B1 ∪ B2) = card B1 + card B2" using B1(2) B2(2) card_Un_disjoint by blast
    hence not_li: "¬ lin_indpt (B1 ∪ B2)"
      using li_le_dim(2)[OF fin, of "B1 ∪ B2"] B1 B2 basis_carriers dim
      unfolding vectorspace.dim_basis[OF subspace_is_vs[OF W1] B1(2) B1(1)]
      unfolding vectorspace.dim_basis[OF subspace_is_vs[OF W2] B2(2) B2(1)]
      by auto

    have "{} ⊆ B1 ∧ lin_indpt ({} ∪ B2)"
      by (metis B2(1) LinearCombinations.module.span_li_not_depend(2) LinearCombinations.submodule_def Un_empty_left
          VectorSpace.subspace_def W2 empty_subsetI local.module.carrier_vs_is_self subspace_is_vs vectorspace.basis_def)
    then obtain B1' where B1': "maximal B1' (λB1'. B1' ⊆ B1 ∧ lin_indpt (B1' ∪ B2))"
      using B1(2) by (metis (no_types, lifting) maximal_exists_superset)

    have "B1' ≠ B1"
      using B1' not_li unfolding maximal_def by fastforce
    then obtain b1 where b1: "b1 ∈ B1 - B1'"
      using B1' by (metis (lifting) maximal_def psubsetI psubset_imp_ex_mem)
    let ?S = "B1' ∪ B2"

    have "lin_dep (?S ∪ {b1})" using b1 B1' unfolding maximal_def by auto
    hence b1_span: "b1 ∈ span ?S"
      using B1'[unfolded maximal_def]
      using lin_dep_iff_in_span[of ?S b1] basis_carriers False b1
      by blast
    also have "… = subspace_sum (span B1') (span B2)"
      using span_union_is_sum[of B1' B2] basis_carriers B1'[unfolded maximal_def] by force
    finally obtain v1' v2 where vs: "b1 = v1' ⊕⇘V⇙ v2 ∧ v1' ∈ span B1' ∧ v2 ∈ span B2"
      unfolding submodule_sum_def by fast

    have "v2 ≠ 𝟬⇘V⇙"
    proof(rule ccontr, clarify)
      assume "v2 = 𝟬⇘V⇙"
      hence "b1 ∈ span B1'" 
        using vs B1' basis_carriers(1)
        by (metis (no_types, lifting) M.add.r_one maximal_def span_closed subset_trans)
      moreover have "lin_indpt B1'"
        using B1' by (metis (lifting) Un_subset_iff maximal_def order.refl supset_ld_is_ld)
      moreover have "¬ lin_dep (B1' ∪ {b1})"
      proof-
        have "B1' ∪ {b1} ⊆ B1"
          using B1' b1 by (metis (lifting) DiffE insert_subset insert_union maximal_def)
        thus ?thesis
          using B1[unfolded vectorspace.basis_def[OF subspace_is_vs[OF W1]]] W1
          by (metis VectorSpace.subspace_def local.module.carrier_vs_is_self span_li_not_depend(2) subset_li_is_li)
      qed
      ultimately show False
        using lin_dep_iff_in_span[of B1' b1] B1' b1 basis_carriers(1)
        by (metis (no_types, lifting) Diff_iff Set.basic_monos(7) basic_trans_rules(23) maximal_def)
    qed
    moreover have "b1 ⊖⇘V⇙ v1' = v2"
      using B1' b1_span basis_carriers vs
      by (smt (verit, ccfv_SIG) M.add.m_comm M.r_neg1 Un_least a_minus_def basic_trans_rules(23)
          local.span_neg maximal_def span_closed)
    moreover have "b1 ⊖⇘V⇙ v1' ∈ span B1"
    proof-
      have "v1' ∈ span B1"
        using vs B1' by (metis (no_types, lifting) maximal_def span_is_monotone subsetD)
      thus ?thesis
        using b1 basis_carriers calculation(2) vs by (metis Diff_iff local.span_add span_closed span_mem)
    qed
    moreover have "v2 ∈ span B2" using vs by fast
    ultimately have "v2 ≠ 𝟬⇘V⇙ ∧ v2 ∈ span B1 ∩ span B2"
      by fastforce
    thus ?thesis
      using B1 B2 W1 W2
      by (metis is_module subspace_is_vs vectorspace.basis_def local.module.carrier_vs_is_self span_li_not_depend(1))
  qed
qed

end

section "Linear Map"

context linear_map
begin

lemma inj_image_lin_indpt:
  assumes "inj_on T (carrier V)"
  assumes "S ⊆ carrier V"
  assumes "V.module.lin_indpt S"
  assumes "finite S"
  shows "W.module.lin_indpt (T`S)"
proof(rule ccontr)
  assume "¬ W.module.lin_indpt (T`S)"
  then obtain B β b where B: "finite B
      ∧ B ⊆ T`S
      ∧ (β ∈ (B → carrier K))
      ∧ (lincomb β B = 𝟬⇘W⇙)
      ∧ (b ∈ B)
      ∧ (β b ≠ 𝟬⇘K⇙)"
    using W.module.lin_dep_def by auto
  define A where "A ≡ {a ∈ S. T a ∈ B}"
  define α where "α ≡ (λv. β (T v))"
  have 1: "inj_on T A"
    by (metis (no_types, lifting) assms(1,2) inj_on_subset A_def mem_Collect_eq subsetI)
  have 2: "A ⊆ carrier V" using A_def assms(2) by blast
  have 3: "α ∈ A → carrier K"
  proof
    fix x assume "x ∈ A"
    moreover then have "T x ∈ carrier W" using 2 by blast
    ultimately show "α x ∈ carrier K" unfolding α_def using B  A_def by blast
  qed
  have 4: "∀v∈A. α v = β (T v)" using α_def by blast
  have 5: "finite A" using A_def assms(4) by force
  have "B = T`A" unfolding A_def using B by blast
  hence "lincomb β B = T (V.module.lincomb α A)" using lincomb_distrib[OF 1 2 3 4 5] by argo
  hence "T (V.module.lincomb α A) = 𝟬⇘W⇙" using B by argo
  moreover have "T (𝟬⇘V⇙) = 𝟬⇘W⇙" by auto
  ultimately have *: "(V.module.lincomb α A) = 𝟬⇘V⇙" using assms(1) by (simp add: 2 3 inj_onD)
  moreover obtain a where "T a = b ∧ a ∈ A" using B ‹B = T ` A› by blast
  moreover then have "α a ≠ 𝟬⇘K⇙" by (simp add: B α_def)
  moreover have "A ⊆ S" using A_def by blast
  ultimately show False using assms(3) 5 3 V.module.lin_dep_def by blast
qed

lemma subspace_restrict:
  assumes "subspace K V' V"
  shows "linear_map K (V.vs V') (W.vs (T`V')) T"
  using assms submodule_restrict[of V']
  by (simp add: field_axioms linear_map_def mod_hom.axioms(1,2) vectorspace_def) 

lemma subspace_image:
  assumes "subspace K V' V"
  shows "subspace K (T`V') W"
  using assms submodule_image[of V'] by (meson subspace_def W.vectorspace_axioms)

lemma inj_subspace_image:
  assumes inj: "inj_on T (carrier V)"
  assumes subspace: "subspace K V' V"
  assumes fin: "V.fin_dim"
  shows "vectorspace.dim K (W.vs (T`V')) = vectorspace.dim K (V.vs V')"
proof-
  interpret restrict: linear_map K "V.vs V'" "W.vs (T`V')" T using subspace_restrict[OF subspace] .
  have inj': "inj_on T (carrier (V.vs V'))"
    using inj subspace by (simp add: submodule_def VectorSpace.subspace_def inj_on_subset)
  have carrier_im: "T`carrier (V.vs V') = carrier (W.vs (T`V'))"
    using V.carrier_vs_is_self W.carrier_vs_is_self by presburger
  show ?thesis
    by (rule restrict.dim_eq[OF V.subspace_fin_dim[OF subspace fin] inj' carrier_im, symmetric])
qed

end

lemma linear_map_mat:
  assumes "A ∈ carrier_mat n m"
  shows "linear_map class_ring (module_vec TYPE('a::{field,ring_1}) m) (module_vec TYPE('a) n) ((*v) A)"
    (is "linear_map ?K ?V ?W ?T") 
proof-
  have "vectorspace ?K ?V" using VS_Connect.vec_vs[of "m"] by blast
  moreover have "vectorspace ?K ?W" using VS_Connect.vec_vs[of "n"] by blast
  moreover have "mod_hom ?K ?V ?W ?T"
  proof-
    have V: "module ?K ?V" by (simp add: vec_module)
    moreover have W: "module ?K ?W" by (simp add: vec_module)
    moreover have "?T ∈ LinearCombinations.module_hom ?K ?V ?W"
    proof-
      have "?T ∈ carrier ?V → carrier ?W" by (metis Pi_I assms mult_mat_vec_carrier vec_space.cV)
      moreover have "∀v1 ∈ carrier ?V. ∀v2 ∈ carrier ?V. ?T (v1 + v2) = ?T v1 + ?T v2"
        by (metis module_vec_def assms monoid_record_simps(1) mult_add_distrib_mat_vec)
      moreover have "∀α ∈ carrier ?K. ∀v ∈ carrier ?V. ?T (α ⋅v v) = α ⋅v (?T v)"
        by (metis assms mult_mat_vec vec_space.cV)
      ultimately show ?thesis unfolding module_vec_def module_hom_def by force
    qed
    ultimately show ?thesis
      unfolding mod_hom_def mod_hom_axioms_def by blast
  qed
  ultimately show ?thesis unfolding linear_map_def by blast
qed

section "Matrix as Linear Map"

locale mat_as_linear_map = 
  fixes A :: "'a::field mat"
  fixes m n
  assumes carrier: "A ∈ carrier_mat m n"
begin

abbreviation "V ≡ module_vec TYPE('a) n"

abbreviation "W ≡ module_vec TYPE('a) m"

sublocale linear_map class_ring V W "(*v) A"
  by (rule linear_map_mat[OF carrier])

abbreviation "f ≡ (*v) A"

end

section "Matrix Isometry"

locale isometry_mat = mat_as_linear_map A m n for A :: "complex mat" and m n +
  assumes isom: "AH * A = 1m n"
begin                         

lemma preserves_norm: "v ∈ carrier_vec n ⟹ vec_norm v = vec_norm (f v)"
  by (metis adjoint_is_conjugate_transpose carrier inner_prod_mult_mat_vec_right isom one_mult_mat_vec
      vec_norm_def)

lemma is_inj: "inj_on f (carrier V)"
  by (smt (verit, ccfv_threshold) adjoint_dim' adjoint_is_conjugate_transpose assoc_mult_mat_vec carrier inj_on_def
      isom one_mult_mat_vec vec_space.cV)

end

section "Compression"

lemma compression_is_hermitian:
  assumes B: "B ∈ carrier_mat n m"
  assumes A: "A ∈ carrier_mat n n" "hermitian A"
  shows "hermitian (BH * A * B)" "BH * A * B ∈ carrier_mat m m"
proof-
  have "((BH * A) * B)H = BH * (BH * A)H"
    using A(1) B
    by (metis adjoint_dim' adjoint_is_conjugate_transpose adjoint_mult mult_carrier_mat)
  also have "… = BH * (AH * BHH)"
    using A(1) assms(1)
    by (metis adjoint_dim' adjoint_is_conjugate_transpose adjoint_mult)
  also have "… = BH * A * B"
    using A B
    by (smt (verit, best) Matrix.transpose_transpose adjoint_dim' adjoint_is_conjugate_transpose
        assoc_mult_mat conjugate_id hermitian_def transpose_conjugate)
  finally show "hermitian (BH * A * B)"
    unfolding hermitian_def adjoint_is_conjugate_transpose[of "BH * A * B"] .
  show "BH * A * B ∈ carrier_mat m m"
    using A(1) B by (meson More_Matrix.carrier_vec_conjugate mult_carrier_mat transpose_carrier_mat)
qed

section "Submatrix"

subsection "Submatrix of Injective Function on Indices, as Compression"

text "Note, with this definition of submatrix, reordering the indices is possible."

definition submatrix_of_inj :: "'a mat ⇒ (nat ⇒ nat) ⇒ (nat ⇒ nat) ⇒ nat ⇒ nat ⇒ 'a mat" where
  "submatrix_of_inj A f g m n ≡ mat m n (λ(i,j). A$$(f i, g j))"

lemma submatrix_of_inj_as_compression:
  fixes A :: "complex mat"
  fixes f :: "nat ⇒ nat"
  assumes A: "A ∈ carrier_mat n n"
  assumes f: "f : {..<m} → {..<n}"
  assumes inj: "inj_on f {..<m}"
  defines "B ≡ submatrix_of_inj A f f m m"
  defines "(Q::complex mat) ≡ mat n m (λ(i,j). if i = f j then 1 else 0)"
  shows "B = QH * A * Q" "isometry_mat Q n m" "Q ∈ carrier_mat n m"
proof-
  show dim: "Q ∈ carrier_mat n m" unfolding Q_def by simp
  note m_leq_n = card_inj[OF f inj, simplified]

  let ?φ = "λj k. if k = f j then 1 else 0"
  have *: "⋀j. j < m ⟹ (∑k<n. cnj (?φ j k) * ?φ j k) = 1"
  proof-
    fix j assume j: "j < m"
    have fj: "f j ∈ {..<n}" using f j by blast
    have "(∑k<n. cnj (?φ j k) * ?φ j k) = (∑k ∈ {..<n} - {f j}. cnj (?φ j k) * ?φ j k) + (cnj (?φ j (f j)) * ?φ j (f j))"
      using fj by (subst sum_diff1, simp_all)
    thus "(∑k<n. cnj (?φ j k) * ?φ j k) = 1" by simp
  qed

  have "⋀i j k. i < m ⟹ j < m ⟹ i ≠ j ⟹ k < n ⟹ cnj (?φ i k) * ?φ j k = 0"
    using inj by (simp add: inj_on_eq_iff)
  hence **: "⋀i j. i < m ⟹ j < m ⟹ i ≠ j ⟹ (∑k<n. cnj (?φ i k) * ?φ j k) = 0"
    by (meson lessThan_iff sum.not_neutral_contains_not_neutral)

  have "QH * Q = 1m m"
    by (rule eq_matI, auto simp: Q_def m_leq_n * **)
  thus "isometry_mat Q n m"
    using dim isometry_mat_axioms_def[of Q m] mat_as_linear_map.intro[of Q n m]
    unfolding isometry_mat_def
    by blast

  have "⋀i j. i < m ⟹ j < m ⟹ row (QH * A) i $ (f j) = A$$(f i, f j)"
  proof-
    fix i j assume i: "i < m" and j: "j < m"
    have fj: "f j < n" using f j m_leq_n by blast
    have "row (QH * A) i $ (f j) = row A (f i) $ (f j)"
      using i fj A f sum_diff1[of "{..<n}" "λk. cnj (if k = f i then 1 else 0) * A $$ (k, f j)" "f i"]
      by (subst adjoint_is_conjugate_transpose) (force simp: Q_def row_def adjoint_def)
    thus "row (QH * A) i $ (f j) = A$$(f i, f j)"
      using A fj by (metis carrier_matD(2) index_vec row_def)
  qed
  hence "⋀i j k. i < m ⟹ j < m ⟹ k < n ⟹ row (QH * A) i $ k * col Q j $ k = (if k = f j then A$$(f i, f j) else 0)"
    unfolding Q_def row_def col_def times_mat_def scalar_prod_def by force
  hence *: "⋀i j. i < dim_row QH ⟹ j < dim_col Q ⟹ B $$ (i, j) = row (QH * A) i ∙ col Q j"
    using dim m_leq_n f by (auto simp: scalar_prod_def B_def submatrix_of_inj_def)

  show "B = QH * A * Q"
    apply (rule eq_matI, simp add: *)
    using dim A by (simp add: B_def submatrix_of_inj_def)+
qed

lemma submatrix_of_inj_as_compression_obt:
  fixes A :: "complex mat"
  fixes f :: "nat ⇒ nat"
  assumes A: "A ∈ carrier_mat n n"
  assumes f: "f : {..<m} → {..<n}"
  assumes inj: "inj_on f {..<m}"
  defines "B ≡ submatrix_of_inj A f f m m"
  obtains Q where "B = QH * A * Q" "isometry_mat Q n m" "Q ∈ carrier_mat n m"
  using submatrix_of_inj_as_compression[OF assms(1-3), folded B_def] by blast

subsection "Submatrix of @{const pick} Function, as Compression"

lemma submatrix_inj:
  assumes A: "A ∈ carrier_mat m n"
  assumes I: "I ⊆ {..<m}"
  assumes J: "J ⊆ {..<n}"
  defines "m' ≡ card I"
  defines "n' ≡ card J"
  defines "B ≡ submatrix A I J"
  defines "f ≡ λi. pick I i"
  defines "g ≡ λj. pick J j"
  shows "f : {..<m'} → {..<m}" "g : {..<n'} → {..<n}"
        "inj_on f {..<m'}" "inj_on g {..<n'}"
        "B = submatrix_of_inj A f g m' n'"
proof-
  show "f : {..<m'} → {..<m}" unfolding f_def m'_def using I(1) pick_in_set_le by fastforce
  show "g : {..<n'} → {..<n}" unfolding g_def n'_def using J(1) pick_in_set_le by fastforce
  show "inj_on f {..<m'}"
    unfolding inj_on_def f_def m'_def by (metis lessThan_iff nat_neq_iff pick_mono_le)
  show "inj_on g {..<n'}"
    unfolding inj_on_def g_def n'_def by (metis lessThan_iff nat_neq_iff pick_mono_le)

  have "card {i. i < dim_row A ∧ i ∈ I} = card I"
    using A I(1)
    by (smt (verit, del_insts) basic_trans_rules(31) carrier_matD(1) equalityI lessThan_iff mem_Collect_eq subsetI)
  moreover have "card {j. j < dim_col A ∧ j ∈ J} = card J"
    using A J(1)
    by (smt (verit, del_insts) basic_trans_rules(31) carrier_matD(2) equalityI lessThan_iff mem_Collect_eq subsetI)
  ultimately show "B = submatrix_of_inj A f g m' n'"
    unfolding submatrix_of_inj_def B_def submatrix_def f_def g_def m'_def n'_def by argo
qed

lemma submatrix_inj_square:
  assumes A: "A ∈ carrier_mat n n"
  assumes I: "I ⊆ {..<n}"
  defines "m ≡ card I"
  defines "B ≡ submatrix A I I"
  defines "f ≡ λi. pick I i"
  shows "f : {..<m} → {..<n}" "inj_on f {..<m}" "B = submatrix_of_inj A f f m m"
  by (rule submatrix_inj[OF A I I, folded m_def B_def f_def])+

lemma submatrix_inj_obt:
  assumes A: "A ∈ carrier_mat m n"
  assumes I: "I ⊆ {..<m}"
  assumes J: "J ⊆ {..<n}"
  defines "m' ≡ card I"
  defines "n' ≡ card J"
  defines "B ≡ submatrix A I J"
  obtains f g where "f : {..<m'} → {..<m}" "g : {..<n'} → {..<n}"
                    "inj_on f {..<m'}" "inj_on g {..<n'}"
                    "B = submatrix_of_inj A f g m' n'"
  using submatrix_inj[OF A I J] m'_def n'_def B_def by blast

lemma submatrix_inj_square_obt:
  assumes A: "A ∈ carrier_mat n n"
  assumes I: "I ⊆ {..<n}"
  defines "m ≡ card I"
  defines "B ≡ submatrix A I I"
  obtains f where "f : {..<m} → {..<n}" "inj_on f {..<m}" "B = submatrix_of_inj A f f m m"
  using submatrix_inj_square[OF A I] m_def B_def by blast

lemma submatrix_as_compression:
  fixes A :: "complex mat"
  fixes f :: "nat ⇒ nat"
  assumes A: "A ∈ carrier_mat n n"
  assumes I: "I ⊆ {..<n}"
  defines "m ≡ card I"
  defines "B ≡ submatrix A I I"
  defines "(Q::complex mat) ≡ mat n m (λ(i,j). if i = pick I j then 1 else 0)"
  shows "B = QH * A * Q" "isometry_mat Q n m" "Q ∈ carrier_mat n m"
  using submatrix_inj_square[OF A I] submatrix_of_inj_as_compression[OF A]
  unfolding m_def B_def Q_def
  by presburger+

lemma submatrix_as_compression_obt:
  fixes A :: "complex mat"
  assumes "A ∈ carrier_mat n n"
  assumes "I ⊆ {..<n}"
  defines "m ≡ card I"
  defines "B ≡ submatrix A I I"
  obtains Q where "B = QH * A * Q" "isometry_mat Q n m"
  using submatrix_as_compression[OF assms(1,2)] m_def B_def by blast

section "Schur Decomposition"

context
  fixes A Λ U :: "'a::{conjugatable_field,real_algebra_1,ord} mat"
  fixes n
  assumes *: "real_diag_decomp A Λ U"
  defines "n ≡ dim_row A"
begin

lemma diag_decomp_inverse: "inverts_mat U (adjoint U)"
  using *
  unfolding real_diag_decomp_def unitary_diag_def unitarily_equiv_def similar_mat_wit_def
  using Complex_Matrix.unitary_def by blast

lemma diag_decomp_invertible: "invertible_mat U"
  using diag_decomp_inverse *
  by (meson Complex_Matrix.unitary_def carrier_matD(2) invertible_mat_def real_diag_decompD(1) square_mat.simps unitaryD2
      unitary_diagD(3))

lemma diag_decomp_cols_distinct: "distinct (cols U)"
  using diag_decomp_invertible *
  by (metis carrier_mat_triv invertible_det invertible_mat_def order_less_irrefl square_mat.simps vec_space.low_rank_det_zero
      vec_space.non_distinct_low_rank)

lemma diag_decomp_ortho: "corthogonal_mat U"
  using unitary_is_corthogonal *
  unfolding real_diag_decomp_def unitary_diag_def unitarily_equiv_def unitary_def
  by blast

private lemma carriers: "A ∈ carrier_mat n n" "U ∈ carrier_mat n n" "UH ∈ carrier_mat n n" "Λ ∈ carrier_mat n n"
  using *[unfolded real_diag_decomp_def unitary_diag_def unitarily_equiv_def similar_mat_wit_def,
      folded adjoint_is_conjugate_transpose]
  using insert_subset n_def
  by metis+

lemma diag_decomp_cols_lin_indpt: "module.lin_indpt class_ring (module_vec TYPE('a) n) (set (cols U))"
  using * carriers unfolding real_diag_decomp_def unitary_diag_def unitary_def n_def
  by (metis diag_decomp_invertible idom_vec.lin_dep_cols_imp_det_0 invertible_det)

lemma real_diag_decomp_eq: "A = U * Λ * UH"
  using *[unfolded real_diag_decomp_def unitary_diag_def unitarily_equiv_def similar_mat_wit_def]
  by (metis adjoint_is_conjugate_transpose)

private lemma U_unitary: "UH * U = 1m n ∧  U * UH = 1m n"
  using *[unfolded real_diag_decomp_def unitary_diag_def unitarily_equiv_def unitary_def]
  using n_def carriers
  by (metis adjoint_is_conjugate_transpose carrier_matD(2) inverts_mat_def similar_mat_wit_def)

private lemma Λ_diag: "diagonal_mat Λ"
  using * real_diag_decompD(1) unitary_diagD(2) by blast

lemma diag_decomp_cols_are_eigenvectors:
  assumes "i < n"
  shows "eigenvector A (col U i) ((diag_mat Λ)!i)" (is "eigenvector A ?v ?μ")
proof-
  have "A *v ?v = (U * Λ) *v (UH *v ?v)"
    using * real_diag_decomp_eq carriers assoc_mult_mat_vec[of "U * Λ" n n "UH" n ?v] by force
  also have "… = (U * Λ) *v (col (1m n) i)"
    using U_unitary n_def assms carriers
    by (metis (mono_tags, lifting) adjoint_is_conjugate_transpose col_mult2)
  also have "… = U *v (Λ *v (unit_vec n i))"
    using * real_diag_decomp_eq carriers col_one assms by fastforce
  also have "… = U *v ((diag_mat Λ)!i ⋅v (unit_vec n i))"
    using *[unfolded real_diag_decomp_def unitary_diag_def]
    using diagonal_unit_vec'[OF carriers(4) Λ_diag, of i] assms carriers(4)
    unfolding diag_mat_def
    by force
  also have "… = (diag_mat Λ)!i ⋅v (U *v (unit_vec n i))"
    using carriers(2) by auto
  also have "… = (diag_mat Λ)!i ⋅v (col U i)"
    by (simp add: assms carriers(2) mat_unit_vec_col)
  finally show ?thesis
    unfolding eigenvector_def
    using assms carriers(2) n_def
    by (metis carrier_matD(1,2) col_dim conjugate_zero_vec corthogonal_matD diag_decomp_ortho scalar_prod_left_zero)
qed

end

section "Rayleigh Quotient"

definition rayleigh_quotient_complex :: "complex mat ⇒ complex vec ⇒ complex" ("ρc") where
  "ρc M x = (QF M x) / (x ∙c x)"

definition rayleigh_quotient :: "complex mat ⇒ complex vec ⇒ real" ("ρ") where
  "ρ M x = Re (ρc M x)"

lemma rayleigh_quotient_negative: "A ∈ carrier_mat n n ⟹ x ∈ carrier_vec n ⟹ ρ A x = - ρ (- A) x"
  by (simp add: rayleigh_quotient_def rayleigh_quotient_complex_def)

lemma rayleigh_quotient_complex_scale:
  fixes k :: real
  assumes "A ∈ carrier_mat n n"
  assumes "v ∈ carrier_vec n"
  assumes "k ≠ 0"
  shows "ρc A v = ρc A (k ⋅v v)"
proof-
  have "ρc A v = (k^2 * ((A *v v) ∙c v)) / (k^2 * (v ∙c v))"
    using assms(3) by (simp add: rayleigh_quotient_complex_def)
  also have "… = (((k ⋅v (A *v v)) ∙c (k ⋅v v))) / (((k ⋅v v) ∙c (k ⋅v v)))"
    using assms(1,2) inner_prod_smult_right by force
  also have "… = ((((A *v (k ⋅v v))) ∙c (k ⋅v v))) / (((k ⋅v v) ∙c (k ⋅v v)))"
    using assms by (metis mult_mat_vec)
  also have "… = ρc A (k ⋅v v)" by (simp add: rayleigh_quotient_complex_def)
  finally show ?thesis .
qed

lemma rayleigh_quotient_scale_complex:
  assumes "A ∈ carrier_mat n n"
  assumes "v ∈ carrier_vec n"
  assumes "k ≠ 0"
  shows "ρ A (k ⋅v v) = ρ A v"
proof-
  let ?v' = "(k ⋅v v)"
  have "(A *v ?v') ∙c ?v' = (cmod k)^2 * (QF A v)"
    using assms(1,2)
    by (smt (verit, ccfv_SIG) cross3_simps(11)inner_prod_smult_left_right mult_conj_cmod_square mult_mat_vec
        mult_mat_vec_carrier quadratic_form_def)
  moreover have "?v' ∙c ?v' = (cmod k)^2 * (v ∙c v)"
    using assms(2) complex_norm_square inner_prod_smult_left_right by force
  ultimately show "ρ A ?v' = ρ A v"
    by (simp add: rayleigh_quotient_def rayleigh_quotient_complex_def assms(3))
qed

lemma rayleigh_quotient_scale_real:
  fixes k :: real
  assumes "A ∈ carrier_mat n n"
  assumes "v ∈ carrier_vec n"
  assumes "k ≠ 0"
  shows "ρ A v = ρ A (k ⋅v v)"
  by (smt (verit, ccfv_SIG) rayleigh_quotient_complex_scale assms Groups.mult_ac(2) complex_norm_square conjugate_complex_def inner_prod_smult_left_right mult_divide_mult_cancel_left_if mult_mat_vec mult_mat_vec_carrier norm_of_real of_real_eq_0_iff power_eq_0_iff quadratic_form_def rayleigh_quotient_complex_def rayleigh_quotient_def)

context cmplx_herm_mat
begin

lemma hermitian_rayleigh_quotient_real:
  assumes "v ∈ carrier_vec n"
  assumes "v ≠ 0v n"
  shows "ρc A v ∈ Reals"
proof-
  have "QF A v ∈ Reals"
    using hermitian_quadratic_form_real assms by blast
  moreover have "inner_prod v v ∈ Reals" by (simp add: self_inner_prod_real)
  moreover have "inner_prod v v ≠ 0" using assms by fastforce
  ultimately show ?thesis unfolding rayleigh_quotient_complex_def using Reals_divide by blast
qed

lemma rayleigh_quotient_QF:
  assumes x: "x ≠ 0v n" "x ∈ carrier_vec n"
  shows "ρ A x = (QF A x) / (vec_norm x)2"
  using hermitian_rayleigh_quotient_real[OF x(2,1)]
  unfolding rayleigh_quotient_def rayleigh_quotient_complex_def vec_norm_def
  using of_real_Re power2_csqrt
  by presburger

end

lemma rayleigh_eigenvector:
  assumes "eigenvector A v e"
  shows "ρ A v = e"
  unfolding rayleigh_quotient_def rayleigh_quotient_complex_def quadratic_form_def
  using assms[unfolded eigenvector_def]
  by force

end