Theory Compact_Operators

section Compact_Operators› -- Finite rank and compact operators›

theory Compact_Operators
  imports Misc_Tensor_Product_BO HS2Ell2
    Sqrt_Babylonian.Sqrt_Babylonian_Auxiliary Wlog.Wlog
    "HOL-Analysis.Abstract_Metric_Spaces"
    Strong_Operator_Topology
    Misc_Tensor_Product_TTS
    Eigenvalues
begin

unbundle cblinfun_notation

subsection ‹Finite rank operators›

definition finite_rank where finite_rank A  A  cspan (Collect rank1)

lemma finite_rank_0[simp]: finite_rank 0
  by (simp add: complex_vector.span_zero finite_rank_def)

lemma finite_rank_scaleC[simp]: finite_rank (c *C a) if finite_rank a
  using complex_vector.span_scale finite_rank_def that by blast

lemma finite_rank_scaleR[simp]: finite_rank (c *R a) if finite_rank a
  by (simp add: scaleR_scaleC that)

lemma finite_rank_uminus[simp]: finite_rank (-a) = finite_rank a
  by (metis add.inverse_inverse complex_vector.span_neg finite_rank_def)

lemma finite_rank_plus[simp]: finite_rank (a + b) if finite_rank a and finite_rank b
  using that by (auto simp: finite_rank_def complex_vector.span_add_eq2)

lemma finite_rank_minus[simp]: finite_rank (a - b) if finite_rank a and finite_rank b
  using complex_vector.span_diff finite_rank_def that(1) that(2) by blast

lemma finite_rank_butterfly[simp]: finite_rank (butterfly x y)
  by (cases x  0  y  0)
     (auto intro: complex_vector.span_base complex_vector.span_zero simp add: finite_rank_def)

lemma finite_rank_sum_butterfly:
  fixes a :: 'a::chilbert_space CL 'b::chilbert_space
  assumes finite_rank a
  shows x y (n::nat). a = (i<n. butterfly (x i) (y i))
proof -
  from assms
  have a  cspan (Collect rank1)
    by (simp add: finite_rank_def)
  then obtain r t where finite t and t_rank1: t  Collect rank1
    and a_sum: a = (at. r a *C a)
    by (smt (verit, best) complex_vector.span_alt mem_Collect_eq)
  from finite t obtain ι and n::nat where ι: bij_betw ι {..<n} t
    using bij_betw_from_nat_into_finite by blast
  define c where c i = r (ι i) *C ι i for i
  from ι t_rank1
  have c_rank1: rank1 (c i)  c i = 0 if i < n for i
    by (auto intro!: rank1_scaleC simp: c_def bij_betw_apply subset_iff that)
  have ac_sum: a = (i<n. c i)
    by (smt (verit, best) a_sum ι c_def sum.cong sum.reindex_bij_betw)
  from c_rank1
  obtain x y where c i = butterfly (x i) (y i) if i < n for i
    apply atomize_elim
    apply (rule SMT_choices)
    using rank1_iff_butterfly by fastforce
  with ac_sum show ?thesis
    by auto
qed    

lemma finite_rank_sum: finite_rank (xF. f x) if x. xF  finite_rank (f x)
  using that by (induction F rule:infinite_finite_induct) (auto intro!: finite_rank_plus)

lemma rank1_finite_rank: finite_rank a if rank1 a
  by (simp add: complex_vector.span_base finite_rank_def that)


lemma finite_rank_compose_left: 
  assumes finite_rank B
  shows finite_rank (A oCL B)
proof -
  from assms have B  cspan (Collect rank1)
    by (simp add: finite_rank_def)
  then obtain F t where finite F and F_rank1: F  Collect rank1 and B = (xF. t x *C x)
    by (smt (verit, best) complex_vector.span_explicit mem_Collect_eq)
  then have A oCL B = (xF. t x *C (A oCL x))
    by (metis (mono_tags, lifting) cblinfun_compose_scaleC_right cblinfun_compose_sum_right sum.cong)
  also have   cspan (Collect finite_rank)
    by (intro complex_vector.span_sum complex_vector.span_scale)
       (use F_rank1 in auto intro!: complex_vector.span_base rank1_finite_rank rank1_compose_left)
  also have  = Collect finite_rank
    by (metis (no_types, lifting) complex_vector.span_superset cspan_eqI finite_rank_def mem_Collect_eq subset_antisym subset_iff)
  finally show ?thesis
    by simp
qed


lemma finite_rank_compose_right:
  assumes finite_rank A
  shows finite_rank (A oCL B)
proof -
  from assms have A  cspan (Collect rank1)
    by (simp add: finite_rank_def)
  then obtain F t where finite F and F_rank1: F  Collect rank1 and A = (xF. t x *C x)
    by (smt (verit, best) complex_vector.span_explicit mem_Collect_eq)
  then have A oCL B = (xF. t x *C (x oCL B))
    by (metis (mono_tags, lifting) cblinfun_compose_scaleC_left cblinfun_compose_sum_left sum.cong)
  also have   cspan (Collect finite_rank)
    by (intro complex_vector.span_sum complex_vector.span_scale)
       (use F_rank1 in auto intro!: complex_vector.span_base rank1_finite_rank rank1_compose_right)
  also have  = Collect finite_rank
    by (metis (no_types, lifting) complex_vector.span_superset cspan_eqI finite_rank_def mem_Collect_eq subset_antisym subset_iff)
  finally show ?thesis
    by simp
qed

lemma rank1_Proj_singleton[iff]: rank1 (Proj (ccspan {x}))
  using Proj_range rank1_def by blast

lemma finite_rank_Proj_singleton[iff]: finite_rank (Proj (ccspan {x}))
  by (simp add: rank1_finite_rank)

lemma finite_rank_Proj_finite_dim:
  fixes S :: 'a::chilbert_space ccsubspace
  assumes finite_dim_ccsubspace S
  shows finite_rank (Proj S)
proof -
  from assms
  obtain B where is_ortho_set B and finite B and spanB: cspan B = space_as_set S
    unfolding finite_dim_ccsubspace.rep_eq
    using cfinite_dim_subspace_has_onb by force
  have Proj S = Proj (ccspan B)
    by (metis Proj.rep_eq finite B cblinfun_apply_inject ccspan_finite spanB)
  moreover have finite_rank (Proj (ccspan B))
    using finite B is_ortho_set B 
  proof induction
    case empty
    then show ?case
      by simp
  next
    case (insert x F)
    then have is_ortho_set F
      by (meson is_ortho_set_antimono subset_insertI)
    have Proj (ccspan (insert x F)) = proj x + Proj (ccspan F)
      by (subst Proj_orthog_ccspan_insert)
         (use insert in auto simp: is_onb_def is_ortho_set_def)
    moreover have finite_rank 
      by (rule finite_rank_plus)
         (auto intro!: is_ortho_set F insert)
    ultimately show ?case 
      by simp
  qed
  ultimately show ?thesis
    by simp
qed

lemma finite_rank_Proj_finite:
  fixes F :: 'a::chilbert_space set
  assumes finite F
  shows finite_rank (Proj (ccspan F))
proof -
  obtain B where is_ortho_set B and finite B and cspan B = cspan F
    by (meson assms orthonormal_basis_of_cspan)
  have Proj (ccspan F) = Proj (ccspan B)
    by (simp add: cspan B = cspan F ccspan.abs_eq)
  moreover have finite_rank (Proj (ccspan B))
    using finite B is_ortho_set B 
  proof induction
    case empty
    then show ?case
      by simp
  next
    case (insert x F)
    then have is_ortho_set F
      by (meson is_ortho_set_antimono subset_insertI)
    have Proj (ccspan (insert x F)) = proj x + Proj (ccspan F)
      by (subst Proj_orthog_ccspan_insert)
         (use insert in auto simp: is_onb_def is_ortho_set_def)
    moreover have finite_rank 
      by (rule finite_rank_plus) (auto intro!: is_ortho_set F insert)
    ultimately show ?case 
      by simp
  qed
  ultimately show ?thesis
    by simp
qed

lemma finite_rank_cfinite_dim[simp]: finite_rank (a :: 'a :: {cfinite_dim,chilbert_space} CL 'b :: complex_normed_vector)
proof -
  obtain B :: 'a set where is_onb B
    using is_onb_some_chilbert_basis by blast
  from is_onb B have [simp]: finite B
    by (auto intro!: cindependent_cfinite_dim_finite is_ortho_set_cindependent simp add: is_onb_def)
  have [simp]: cspan B = UNIV
  proof -
    from is_onb B have ccspan B = 
      using is_onb_def by blast
    then have closure (cspan B) = UNIV
      by (metis ccspan.rep_eq space_as_set_top)
    then show ?thesis
      by simp
  qed
  have a_sum: a = (bB. a oCL selfbutter b)
  proof (rule cblinfun_eq_on_UNIV_span[OF cspan B = UNIV])
    fix s assume [simp]: s  B
    with is_onb B have norm s = 1
      by (simp add: is_onb_def)
    have 1: j  s  j  B  (a oCL selfbutter j) *V s = 0 for j
      using is_onb B s  B cblinfun.scaleC_right is_onb_def is_ortho_set_def scaleC_eq_0_iff
      by fastforce
    have 2: a *V s = (if s  B then (a oCL selfbutter s) *V s else 0)
      using norm s = 1 s  B by (simp add: cnorm_eq_1)
    show a *V s = (bB. a oCL selfbutter b) *V s
      by (subst cblinfun.sum_left, subst sum_single[where i=s]) (use 1 2 in auto)
  qed
  have finite_rank (bB. a oCL selfbutter b)
    by (auto intro!: finite_rank_sum simp: cblinfun_comp_butterfly)
  with a_sum show ?thesis
    by simp
qed

lemma finite_rank_cspan_butterflies:
  finite_rank a  a  cspan (range (case_prod butterfly))
  for a :: 'a::chilbert_space CL 'b::chilbert_space
proof -
  have (Collect finite_rank :: ('a CL 'b) set) = cspan (Collect rank1)
    using finite_rank_def by fastforce
  also have  = cspan (insert 0 (Collect rank1))
    by force
  also have  = cspan (range (case_prod butterfly))
    by (rule arg_cong[where f=cspan])
       (use butterfly_0_left in force simp: image_iff rank1_iff_butterfly simp del: butterfly_0_left)+
  finally show ?thesis
    by auto
qed


lemma finite_rank_comp_left: finite_rank (a oCL b) if finite_rank a
  for a b :: _::chilbert_space CL _::chilbert_space
proof -
  from that
  have a  cspan (range (case_prod butterfly))
    by (simp add: finite_rank_cspan_butterflies)
  then have a oCL b  (λa. a oCL b) ` cspan (range (case_prod butterfly))
    by fast
  also have  = cspan ((λa. a oCL b) ` range (case_prod butterfly))
    by (simp add: clinear_cblinfun_compose_left complex_vector.linear_span_image)
  also have   cspan (range (case_prod butterfly))
    by (force intro!: complex_vector.span_mono
              simp add: image_image case_prod_unfold butterfly_comp_cblinfun image_def)
  finally show ?thesis
    using finite_rank_cspan_butterflies by blast
qed


lemma finite_rank_comp_right: finite_rank (a oCL b) if finite_rank b
  for a b :: _::chilbert_space CL _::chilbert_space
proof -
  from that
  have b  cspan (range (case_prod butterfly))
    by (simp add: finite_rank_cspan_butterflies)
  then have a oCL b  ((oCL) a) ` cspan (range (case_prod butterfly))
    by fast
  also have  = cspan (((oCL) a) ` range (case_prod butterfly))
    by (simp add: clinear_cblinfun_compose_right complex_vector.linear_span_image)
  also have   cspan (range (case_prod butterfly))
    by (force intro!: complex_vector.span_mono
              simp add: image_image case_prod_unfold cblinfun_comp_butterfly image_def)
  finally show ?thesis
    using finite_rank_cspan_butterflies by blast
qed



subsection ‹Compact operators›

definition compact_map where compact_map f  clinear f  compact (closure (f ` cball 0 1))

lemma bounded_clinear f if compact_map f
  ― ‹citeconway2013course, Proposition II.4.2 (a)›
  thm bounded_clinear_def
proof (unfold bounded_clinear_def bounded_clinear_axioms_def, intro conjI)
  show clinear f
    using compact_map_def that by blast
  have compact (closure (f ` cball 0 1))
    using compact_map_def that by blast
  then have bounded (f ` cball 0 1)
    by (meson bounded_subset closure_subset compact_imp_bounded)
  then obtain K where *: norm (f x)  K if norm x  1 for x
    by (force simp: bounded_iff dist_norm ball_def)
  have norm (f x)  norm x * K for x
  proof (cases x = 0)
    case True
    then show ?thesis
      using clinear f complex_vector.linear_0 by force
  next
    case False
    have norm (f x) = norm (f (norm x *C sgn x))
      by simp
    also have  = norm x * norm (f (sgn x))
      by (smt (verit, best) clinear f complex_vector.linear_scale norm_ge_zero norm_of_real norm_scaleC)
    also have   norm x * K
      by (simp add: "*" mult_left_mono norm_sgn)
    finally show ?thesis
      by -
  qed
  then show K. x. norm (f x)  norm x * K
    by blast
qed

lift_definition compact_op :: ('a::complex_normed_vector CL 'b::complex_normed_vector)  bool is compact_map.

lemma compact_op_def2: compact_op a  compact (closure (a ` cball 0 1))
  by transfer (use bounded_clinear.clinear compact_map_def in blast)

lemma compact_op_0[simp]: compact_op 0
  by (simp add: compact_op_def2 image_constant[where x=0] mem_cball_leI[where x=0])

lemma compact_op_scaleC[simp]: compact_op (c *C a) if compact_op a
proof -
  have compact (closure (a ` cball 0 1))
    using compact_op_def2 that by blast
  then have *: compact (scaleC c ` closure (a ` cball 0 1))
    using compact_scaleC by blast
  have closure ((c *C a) ` cball 0 1) = closure (scaleC c ` a ` cball 0 1)
    by (metis (no_types, lifting) cblinfun.scaleC_left image_cong image_image)
  also have  = scaleC c ` closure (a ` cball 0 1)
    using closure_scaleC by blast
  finally have compact (closure ((c *C a) ` cball 0 1))
    using "*" by simp
  then show ?thesis
    using compact_op_def2 by blast
qed

lemma compact_op_scaleR[simp]: compact_op (c *R a) if compact_op a
  by (simp add: scaleR_scaleC that)

lemma compact_op_uminus[simp]: compact_op (-a) = compact_op a
  by (metis compact_op_scaleC scaleC_minus1_left verit_minus_simplify(4))

lemma compact_op_plus[simp]: compact_op (a + b) if compact_op a and compact_op b
proof -
  have compact (closure (a ` cball 0 1))
    using compact_op_def2 that by blast
  moreover have compact (closure (b ` cball 0 1))
    using compact_op_def2 that by blast
  ultimately have compact_sum: 
    compact {x + y |x y. x  closure ((*V) a ` cball 0 1) 
                         y  closure ((*V) b ` cball 0 1)} (is compact ?sum)
    by (rule compact_sums)
  have compact (closure ((a + b) ` cball 0 1))
  proof -
    have ((*V) (a + b) ` cball 0 1)  ?sum
      using cblinfun.real.add_left closure_subset image_subset_iff by blast
    then have closure ((*V) (a + b) ` cball 0 1)  closure ?sum
      by (meson closure_mono)
    also have  = ?sum
      using compact_sum
      by (auto intro!: closure_closed compact_imp_closed)
    finally show ?thesis
      by (rule compact_closed_subset[rotated 2]) (use compact_sum in auto)
  qed
  then show ?thesis
    using compact_op_def2 by blast
qed

lemma csubspace_compact_op: csubspace (Collect compact_op)
  ― ‹citeconway2013course, Proposition II.4.2 (b)›
  by (simp add: complex_vector.subspace_def)

lemma compact_op_minus[simp]: compact_op (a - b) if compact_op a and compact_op b
  by (metis compact_op_plus compact_op_uminus that(1) that(2) uminus_add_conv_diff)

lemma compact_op_sgn[simp]: compact_op (sgn a) = compact_op a
proof (cases a = 0)
  case True
  then show ?thesis
    by simp
next
  case False
  have compact_op (sgn a) if compact_op a
    by (simp add: sgn_cblinfun_def that)
  moreover have compact_op (norm a *R sgn a) if compact_op (sgn a)
    by (simp add: that)
  moreover have norm a *R sgn a = a
    by (simp add: False sgn_div_norm)
  ultimately show ?thesis
    by auto
qed

lemma closed_compact_op: 
  shows closed (Collect (compact_op :: ('a::complex_normed_vector CL 'b::chilbert_space)  bool))
  ― ‹citeconway2013course, Proposition II.4.2 (b)›
proof (intro closed_sequential_limits[THEN iffD2] allI impI conjI)
  fix T and A :: 'a CL 'b
  assume asm: (n. T n  Collect compact_op)  T  A
  have Met_TC.mtotally_bounded (A ` cball 0 1)
  proof (unfold Met_TC.mtotally_bounded_def, intro allI impI)
    fix ε :: real assume ε > 0
    define δ where δ = ε/3
    then have δ > 0
      using ε > 0 by simp
    from asm[unfolded LIMSEQ_def, THEN conjunct2, rule_format, OF δ > 0]
    obtain n where dist_TA: dist (T n) A < δ
      by auto
    from asm have compact_op (T n)
      by simp
    then have Met_TC.mtotally_bounded (T n ` cball 0 1)
      by (subst Met_TC.mtotally_bounded_eq_compact_closure_of)
         (auto intro!: simp: compact_op_def2 Met_TC.mtotally_bounded_eq_compact_closure_of)
    then obtain K where finite K and K_T: K  T n ` cball 0 1 and 
      TK: T n ` cball 0 1  (kK. Met_TC.mball k δ) 
      unfolding Met_TC.mtotally_bounded_def using δ > 0 by meson
    from finite K and K_T obtain H where finite H and H  cball 0 1
      and KTH: K = T n ` H
      by (meson finite_subset_image)
    from TK have TH: T n ` cball 0 1  (hH. ball (T n *V h) δ)
      by (simp add: KTH)
    have A ` cball 0 1  (hH. ball (A h) ε)
    proof (rule subsetI)
      fix x assume x  (*V) A ` cball 0 1
      then obtain l where l  cball 0 1 and xl: x = A l
        by blast
      then have T n l  T n ` cball 0 1
        by auto
      with TH obtain h where h  H and T n l  ball (T n h) δ
        by blast
      then have dist_Tlh: dist (T n l) (T n h) < δ
        by (simp add: dist_commute)
      have dist (A h) (A l) < ε
      proof -
        have norm_h: norm h  1
          using H  cball 0 1 h  H mem_cball_0 by blast
        have norm_l: norm l  1
          using l  cball 0 1 by auto
        have dist (A h) (T n h) < δ
        proof -
          have dist (T n *V h) (A *V h)  norm h * dist (T n) A
            using norm_cblinfun[of "T n - A" h] by (simp add: dist_norm cblinfun.diff_left mult_ac)
          also have   1 * dist (T n) A
            by (rule mult_right_mono) (use norm_h in auto)
          also have dist (T n) A < δ
            by fact
          finally show ?thesis
            by (simp add: dist_commute)
        qed
        moreover have dist (T n h) (T n l) < δ
          using dist_Tlh by (metis dist_commute)
        moreover from dist_TA norm_l have dist (T n l) (A l) < δ
        proof -
          have dist (T n *V l) (A *V l)  norm l * dist (T n) A
            using norm_cblinfun[of "T n - A" l] by (simp add: dist_norm cblinfun.diff_left mult_ac)
          also have   1 * dist (T n) A
            by (rule mult_right_mono) (use norm_l in auto)
          also have dist (T n) A < δ
            by fact
          finally show ?thesis
            by (simp add: dist_commute)
        qed
        ultimately show ?thesis
          unfolding δ_def
          by (rule dist_triangle_third)
      qed
      then show x  (hH. ball (A h) ε)
        using h  H by (auto intro!: simp: xl)
    qed
    then show K. finite K  K  (*V) A ` cball 0 1  
              (*V) A ` cball 0 1  (xK. Met_TC.mball x ε)
      using H  cball 0 1
      by (force intro!: exI[of _ A ` H] finite H simp: ball_def)
  qed
  then have Met_TC.mtotally_bounded (closure (A ` cball 0 1))
    using Met_TC.mtotally_bounded_closure_of by auto
  then have compact (closure (A ` cball 0 1))
    by (simp_all add: Met_TC.mtotally_bounded_eq_compact_closure_of complete_UNIV_cuspace)
  then show A  Collect compact_op
    using compact_op_def2 by blast
qed

lemma rank1_compact_op: compact_op a if rank1 a
proof -
  wlog a  0
    using negation by simp
  with that obtain ψ where im_a: a *S  = ccspan {ψ} and ψ  0
    using rank1_def by fastforce
  define c where c = norm a / norm ψ
  have compact_ψc: compact ((λx. x *C ψ) ` cball 0 c)
  proof -
    have continuous_on (cball 0 c) (λx. x *C ψ)
      by (auto intro!: continuous_at_imp_continuous_on)
    moreover have compact (cball (0::complex) c)
      by (simp add: compact_eq_bounded_closed)
    ultimately show ?thesis
      by (rule compact_continuous_image)
  qed
  have a ` cball 0 1  (λx. x *C ψ) ` cball 0 c
  proof (rule subsetI)
    fix φ
    assume asm: φ  a ` cball 0 1
    then have φ  space_as_set (a *S )
      using cblinfun_apply_in_image by blast
    also have  = cspan {ψ}
      by (simp add: ccspan.rep_eq im_a)
    finally obtain d where d: φ = d *C ψ
      by (metis complex_vector.span_breakdown_eq complex_vector.span_empty eq_iff_diff_eq_0 singletonD)
    from asm obtain γ where φ = a γ and norm γ  1
      by force
    have cmod d * norm ψ = norm φ
      by (simp add: d)
    also have   norm a * norm γ
      using φ = a *V γ complex_of_real_mono norm_cblinfun by blast
    also have   norm a
      by (metis norm γ  1 mult.commute mult_left_le_one_le norm_ge_zero)
    finally have cmod d  c
      by (smt (verit, ccfv_threshold) ψ  0 c_def linordered_field_class.pos_divide_le_eq nonzero_eq_divide_eq norm_le_zero_iff)
    then show φ  (λx. x *C ψ) ` cball 0 c
      by (auto simp: d)
  qed
  with compact_ψc have cl_in_cl: closure (a ` cball 0 1)  ((λx. x *C ψ) ` cball 0 c)
    using closure_mono[of _ ((λx. x *C ψ) ` cball 0 c)] compact_ψc
    by (simp add: compact_imp_closed)
  with compact_ψc show compact_op a
    using compact_closed_subset compact_op_def2 by blast
qed

lemma finite_rank_compact_op: compact_op a if finite_rank a
proof -
  from that obtain t r where finite t and t  Collect rank1
    and a_decomp: a = (xt. r x *C x)
    by (auto simp: finite_rank_def complex_vector.span_explicit)
  from finite t t  Collect rank1 show compact_op a
    by (unfold a_decomp, induction)
       (auto intro!: compact_op_plus compact_op_scaleC intro: rank1_compact_op)
qed

lemma bounded_products_sot_lim_imp_lim:
  ― ‹Implicit in the proof of citeconway2013course, Proposition II.4.4 (c)›
  fixes A :: 'a::complex_normed_vector CL 'b::chilbert_space
  assumes lim_PA: limitin cstrong_operator_topology (λx. P x oCL A) A F
    and compact_op A
    and P_leq_B: x. norm (P x)  B
  shows ((λx. P x oCL A)  A) F
proof -
  wlog F  
    using negation by simp
  wlog B  0
  proof -
    from negation assms have P0: P x = 0 for x
      by auto
    from lim_PA have ((λx. 0)  Abs_cblinfun_sot A) F
      unfolding limitin_canonical_iff [symmetric]
      by (transfer fixing: P F) (use P0 in simp)
    moreover have ((λx. 0)  0) F
      by simp
    ultimately have Abs_cblinfun_sot A = 0
      using F   tendsto_unique by blast
    then have A = 0
      by (metis Abs_cblinfun_sot_inverse cstrong_operator_topology_topspace lim_PA
                limitin_def zero_cblinfun_sot.rep_eq)
    with P0 show ?thesis
      by simp
  qed
  have B > 0
  proof -
    from P_leq_B[of undefined] have B  0
      by (smt (verit, del_insts) norm_ge_zero)
    with B  0
    show ?thesis
      by simp
  qed

  show ?thesis
  proof (rule metric_space_class.tendstoI)
    fix ε :: real assume ε > 0
    define δ γ T where δ = ε/4 and γ = min δ (δ/B) and T x = P x oCL A for x
    then have δ > 0
      using ε > 0 by simp
    then have γ > 0
      using B > 0 by (simp add: γ_def)
    from compact_op A have Met_TC.mtotally_bounded (A ` cball 0 1)
      by (subst Met_TC.mtotally_bounded_eq_compact_closure_of)
         (auto intro!: simp: compact_op_def2 Met_TC.mtotally_bounded_eq_compact_closure_of)
    then obtain K where finite K and K_T: K  A ` cball 0 1 and 
      AK: A ` cball 0 1  (kK. Met_TC.mball k γ) 
      unfolding Met_TC.mtotally_bounded_def using γ > 0 by meson
    from finite K and K_T obtain H where finite H and H  cball 0 1
      and KAH: K = A ` H
      by (meson finite_subset_image)
    from AK have AH: A ` cball 0 1  (hH. ball (A *V h) γ)
      by (simp add: KAH)
    have F x in F. hH. dist (T x h) (A h) < δ
      using lim_PA δ > 0
      by (auto intro!: eventually_ball_finite finite H
          simp: limitin_cstrong_operator_topology T_def metric_space_class.tendsto_iff)
    then show F x in F. dist (T x) A < ε
    proof (rule eventually_mono)
      fix x
      assume asm: hH. dist (T x *V h) (A *V h) < δ
      have dist (T x l) (A l)  3 * δ if norm l = 1 for l
      proof -
        from that have A l  A ` cball 0 1
          by auto
        with AH obtain h where h  H and Alγ: A l  ball (A h) γ
          by blast
        then have dist_Alh: dist (A l) (A h) < γ
          by (simp add: dist_commute)
        have dist (A l) (A h) < δ
          using dist_Alh by (simp add: γ_def)
        moreover from asm have dist (A h) (T x h) < δ
          by (simp add: h  H dist_commute)
        moreover have dist (T x h) (T x l) < δ
        proof -
          have dist (T x h) (T x l)  norm (P x) * dist (A h) (A l)
            by (metis T_def cblinfun.real.diff_right cblinfun_apply_cblinfun_compose
                      dist_norm norm_cblinfun)
          also from Alγ P_leq_B have  < B * γ
            by (smt (verit, ccfv_SIG) B  0 linordered_semiring_strict_class.mult_le_less_imp_less linordered_semiring_strict_class.mult_strict_mono' mem_ball norm_ge_zero zero_le_dist)
          also have   B * (δ / B)
            by (smt (verit, best) γ_def 0 < B mult_left_mono)
          also have   δ
            by (simp add: B  0)
          finally show ?thesis
            by -
        qed
        ultimately show ?thesis
          by (smt (verit) dist_commute dist_triangle2)
      qed
      then have dist (T x) A  3 * δ
        unfolding dist_norm using δ > 0
        by (auto intro!: norm_cblinfun_bound_unit simp: cblinfun.diff_left)
      then show dist (T x) A < ε
        by (rule order.strict_trans1) (use ε > 0 in simp add: δ_def)
    qed
  qed
qed


lemma compact_op_finite_rank: 
  fixes A :: 'a::complex_normed_vector CL 'b::chilbert_space
  shows compact_op A  A  closure (Collect finite_rank)
  ― ‹citeconway2013course, Proposition II.4.4 (c)›
proof (rule iffI)
  assume A  closure (Collect finite_rank)
  then have A  closure (Collect compact_op)
    by (metis closure_sequential finite_rank_compact_op mem_Collect_eq)
  also have  = Collect compact_op
    by (simp add: closed_compact_op)
  finally show compact_op A
    by simp
next
  assume compact_op A
  then have compact (closure (A ` cball 0 1))
    using compact_op_def2 by blast
  then have sep_A_ball: separable (closure (A ` cball 0 1))
    using compact_imp_separable by blast
  define L where L = closure (range A)
  obtain B :: nat  _ where L  closure (range B)
  proof atomize_elim
    from sep_A_ball obtain B0 where countable B0
      and A_B0: A ` cball 0 1  closure B0
      by (meson closure_subset order_trans separable_def)
    define B1 where B1 = (n::nat. scaleR n ` B0)
    from countable B0 have countable B1
      by (auto intro!: countable_UN countable_image simp: B1_def)
    have range A = (n::nat. A ` scaleR n ` cball (0::'a) 1)
    proof -
      have UNIV = (n::nat. scaleR n ` cball (0::'a) 1)
      proof (intro antisym subsetI UNIV_I)
        fix x :: 'a
        have "norm x < 1 + real_of_int norm x" "1 + real_of_int norm x > 0"
          using norm_ge_zero[of x] by linarith+
        hence x  scaleR (nat (ceiling (norm x)) + 1) ` cball (0::'a) 1
          by (intro image_eqI[where x=x /R (nat (ceiling (norm x)) + 1)])
             (auto simp: divide_simps)
        then show x  (x::nat. (*R) (real x) ` cball 0 1)
          by blast
      qed
      then show ?thesis
        by fastforce
    qed
    also have  = (n::nat. scaleR n ` A ` cball 0 1)
      by (auto simp: cblinfun.scaleR_right image_comp fun_eq_iff)
    also have   (n::nat. scaleR n ` closure B0)
      using A_B0 by fastforce
    also have   closure (n::nat. scaleR n ` B0)
      by (metis (mono_tags, lifting) SUP_le_iff closure_closure closure_mono closure_scaleR closure_subset)
    also have  = closure B1
      using B1_def by fastforce
    finally have L  closure B1
      by (simp add: L_def closure_minimal)
    with countable B1
    show B :: nat  _. L  closure (range B)
      by (metis L_def closure_eq_empty empty_not_UNIV image_is_empty range_from_nat_into subset_empty)
  qed
  define P T where P n = Proj (ccspan (B ` {..n})) 
    and T n = P n oCL A for n
  have limitin cstrong_operator_topology T A sequentially
  proof (intro limitin_cstrong_operator_topology[THEN iffD2, rule_format] metric_LIMSEQ_I)
(* Idea: Every point in L is close to some point B n.
   Then it must be even closer to ccspan (B ` {..n}).
   Then the proj must be close to that, too. *)
    fix h and ε :: real assume ε > 0
    define Ah where Ah = A h
    have Ah  closure (range B)
      by (metis L_def Ah_def L  closure (range B) cblinfun_apply_in_image
                cblinfun_image.rep_eq subsetD top_ccsubspace.rep_eq)
    then obtain x where x  range B and dist x Ah < ε
      using ε > 0 unfolding closure_approachable by blast
    then obtain n0 where x_n0: x = B n0
      by blast
    have dist (P n *V Ah) Ah < ε if n  n0 for n
    proof -
      have x  space_as_set (P n *S )
        using n  n0
        by (auto intro!: ccspan_superset' simp: P_def x_n0)
      from Proj_nearest[OF this, of Ah]
      have dist (P n *V Ah) Ah  dist x Ah
        by (simp add: P_def)
      with dist x Ah < ε show ?thesis
        by auto
    qed  
    then show n0. nn0. dist (T n *V h) (A *V h) < ε
      unfolding T_def Ah_def by auto
  qed
  then have ((λx. P x oCL A)  A) sequentially
    unfolding T_def
    by (auto intro!: bounded_products_sot_lim_imp_lim[where B=1] compact_op A norm_is_Proj
        simp: P_def)
  moreover have finite_rank (P x oCL A) for x
    by (auto intro!: finite_rank_compose_right finite_rank_Proj_finite simp: P_def)
  ultimately show A  closure (Collect finite_rank)
    using closure_sequential by force
qed

typedef (overloaded) ('a::chilbert_space,'b::complex_normed_vector) compact_op =
    Collect compact_op :: ('a CL 'b) set
  morphisms from_compact_op Abs_compact_op
  by (auto intro!: exI[of _ 0])
setup_lifting type_definition_compact_op

instantiation compact_op :: (chilbert_space, complex_normed_vector) complex_normed_vector begin
lift_definition scaleC_compact_op :: complex  ('a, 'b) compact_op  ('a, 'b) compact_op is scaleC by simp
lift_definition uminus_compact_op :: ('a, 'b) compact_op  ('a, 'b) compact_op is uminus by simp
lift_definition zero_compact_op :: ('a, 'b) compact_op is 0 by simp
lift_definition minus_compact_op :: ('a, 'b) compact_op  ('a, 'b) compact_op  ('a, 'b) compact_op is minus by simp
lift_definition plus_compact_op :: ('a, 'b) compact_op  ('a, 'b) compact_op  ('a, 'b) compact_op is plus by simp
lift_definition sgn_compact_op :: ('a, 'b) compact_op  ('a, 'b) compact_op is sgn by simp
lift_definition norm_compact_op :: ('a, 'b) compact_op  real is norm .
lift_definition scaleR_compact_op :: real  ('a, 'b) compact_op  ('a, 'b) compact_op is scaleR by simp
lift_definition dist_compact_op :: ('a, 'b) compact_op  ('a, 'b) compact_op  real is dist .
definition [code del]:
  (uniformity :: (('a, 'b) compact_op × ('a, 'b) compact_op) filter) = (INF e{0 <..}. principal {(x, y). dist x y < e})
definition open_compact_op :: "('a, 'b) compact_op set  bool"
  where [code del]: "open_compact_op S = (xS. F (x', y) in uniformity. x' = x  y  S)"
instance
proof
  show "((*R) r :: ('a, 'b) compact_op  _) = (*C) (complex_of_real r)" for r
    by (rule ext, transfer) (simp add: scaleR_scaleC)
  show "a + b + c = a + (b + c)"
    for a b c :: "('a, 'b) compact_op"
    by transfer simp
  show "a + b = b + a"
    for a b :: "('a, 'b) compact_op"
    by transfer simp
  show "0 + a = a"
    for a :: "('a, 'b) compact_op"
    by transfer simp
  show "- (a::('a, 'b) compact_op) + a = 0"
    for a :: "('a, 'b) compact_op"
    by transfer simp
  show "a - b = a + - b"
    for a b :: "('a, 'b) compact_op"
    by transfer simp
  show "a *C (x + y) = a *C x + a *C y"
    for a :: complex and x y :: "('a, 'b) compact_op"
    by transfer (simp add: scaleC_add_right)
  show "(a + b) *C x = a *C x + b *C x"
    for a b :: complex and x :: "('a, 'b) compact_op"
    by transfer (simp add: scaleC_left.add)
  show "a *C b *C x = (a * b) *C x"
    for a b :: complex and x :: "('a, 'b) compact_op"
    by transfer simp
  show "1 *C x = x"
    for x :: "('a, 'b) compact_op"
    by transfer simp
  show "dist x y = norm (x - y)"
    for x y :: "('a, 'b) compact_op"
    by transfer (simp add: dist_norm)
  show "a *R (x + y) = a *R x + a *R y"
    for a :: real and x y :: "('a, 'b) compact_op"
    by transfer (simp add: scaleR_right_distrib)
  show "(a + b) *R x = a *R x + b *R x"
    for a b :: real and x :: "('a, 'b) compact_op"
    by transfer (simp add: scaleR_left.add)
  show "a *R b *R x = (a * b) *R x"
    for a b :: real and x :: "('a, 'b) compact_op"
    by transfer simp
  show "1 *R x = x"
    for x :: "('a, 'b) compact_op"
    by transfer simp
  show "sgn x = inverse (norm x) *R x"
    for x :: "('a, 'b) compact_op"
    by transfer (simp add: sgn_div_norm)
  show "uniformity = (INF e{0<..}. principal {(x, y). dist (x::('a, 'b) compact_op) y < e})"
    using uniformity_compact_op_def by blast
  show "open U = (xU. F (x', y) in uniformity. x' = x  y  U)"
    for U :: "('a, 'b) compact_op set"
    by (simp add: open_compact_op_def)
  show "(norm x = 0)  (x = 0)"
    for x :: "('a, 'b) compact_op"
    by transfer simp
  show "norm (x + y)  norm x + norm y"
    for x y :: "('a, 'b) compact_op"
    by transfer (use norm_triangle_ineq in blast)
  show "norm (a *R x) = ¦a¦ * norm x"
    for a :: real and x :: "('a, 'b) compact_op"
    by transfer simp
  show "norm (a *C x) = cmod a * norm x"
    for a :: complex and x :: "('a, 'b) compact_op"
    by transfer simp
qed
end (* instantiation compact_op :: complex_normed_vector *)


lemma from_compact_op_plus: from_compact_op (a + b) = from_compact_op a + from_compact_op b
  by transfer simp

lemma from_compact_op_scaleC: from_compact_op (c *C a) = c *C from_compact_op a
  by transfer simp

lemma from_compact_op_norm[simp]: norm (from_compact_op a) = norm a
  by transfer simp

lemma compact_op_butterfly[simp]: compact_op (butterfly x y)
  by (simp add: finite_rank_compact_op)

lift_definition butterfly_co :: 'a::complex_normed_vector  'b::chilbert_space  ('b,'a) compact_op is butterfly
  by simp

lemma butterfly_co_add_left: butterfly_co (a + a') b = butterfly_co a b + butterfly_co a' b
  by transfer (rule butterfly_add_left)

lemma butterfly_co_add_right: butterfly_co a (b + b') = butterfly_co a b + butterfly_co a b'
  by transfer (rule butterfly_add_right)

lemma butterfly_co_scaleR_left[simp]: "butterfly_co (r *R ψ) φ = r *C butterfly_co ψ φ"
  by transfer (rule butterfly_scaleR_left)

lemma butterfly_co_scaleR_right[simp]: "butterfly_co ψ (r *R φ) = r *C butterfly_co ψ φ"
  by transfer (rule butterfly_scaleR_right)

lemma butterfly_co_scaleC_left[simp]: "butterfly_co (r *C ψ) φ = r *C butterfly_co ψ φ"
  by transfer (rule butterfly_scaleC_left)

lemma butterfly_co_scaleC_right[simp]: "butterfly_co ψ (r *C φ) = cnj r *C butterfly_co ψ φ"
  by transfer (rule butterfly_scaleC_right)

lemma finite_rank_separating_on_compact_op:
  fixes F G :: ('a::chilbert_space,'b::chilbert_space) compact_op  'c::complex_normed_vector
  assumes x. finite_rank (from_compact_op x)  F x = G x
  assumes bounded_clinear F
  assumes bounded_clinear G
  shows F = G
proof -
  define FG where FG x = F x - G x for x
  from bounded_clinear F and bounded_clinear G
  have bounded_clinear FG
    by (auto simp: FG_def[abs_def] intro!: bounded_clinear_sub)
  then have contFG': continuous_map euclidean euclidean FG
    by (simp add: Complex_Vector_Spaces.bounded_clinear.bounded_linear linear_continuous_on)
  have continuous_on (Collect compact_op) (FG o Abs_compact_op)
  proof
    fix a :: "'a CL 'b" and e :: real
    assume "0 < e" and a_compact: "a  Collect compact_op"
    have dist_rw: dist x' a = dist (Abs_compact_op x') (Abs_compact_op a) if compact_op x' for x'
      by (metis Abs_compact_op_inverse a_compact dist_compact_op.rep_eq mem_Collect_eq that)

    from bounded_clinear FG
    have continuous_on UNIV FG
      using contFG' continuous_map_iff_continuous2 by blast
    then have d>0. x'. dist x' (Abs_compact_op a) < d  dist (FG x') (FG (Abs_compact_op a))  e
      using e > 0 by (force simp: continuous_on_iff)
    then have d>0. x'. compact_op x'  dist (Abs_compact_op x') (Abs_compact_op a) < d  
                  dist (FG (Abs_compact_op x')) (FG (Abs_compact_op a))  e
      by blast
    then show "d>0. x'Collect compact_op. dist x' a < d  dist ((FG  Abs_compact_op) x') ((FG  Abs_compact_op) a)  e"
      by (simp add: dist_rw o_def)
  qed
  then have contFG: continuous_on (closure (Collect finite_rank)) (FG o Abs_compact_op)
    by (auto simp: compact_op_finite_rank[abs_def])

  have FG0: finite_rank a  (FG o Abs_compact_op) a = 0 for a
    by (metis (no_types, lifting) Abs_compact_op_inverse FG_def assms(1) closure_subset comp_apply compact_op_finite_rank eq_iff_diff_eq_0 mem_Collect_eq subset_eq)

  have (FG o Abs_compact_op) a = 0 if compact_op a for a
    using contFG FG0
    by (rule continuous_constant_on_closure) (use that in auto simp: compact_op_finite_rank)

  then have FG a = 0 for a
    by (metis Abs_compact_op_cases comp_apply mem_Collect_eq)

  then show F = G
    by (auto simp: FG_def[abs_def] fun_eq_iff)
qed

lemma trunc_ell2_as_Proj: trunc_ell2 S ψ = Proj (ccspan (ket ` S)) ψ
proof (rule cinner_ket_eqI)
  fix x
  have *: Proj (ccspan (ket ` S)) (ket x) = 0 if x  S
    by (auto intro!: Proj_0_compl mem_ortho_ccspanI simp: that)
  have ket x C trunc_ell2 S ψ = of_bool (xS) * (ket x C ψ)
    by (simp add: cinner_ket_left trunc_ell2.rep_eq)
  also have  = Proj (ccspan (ket ` S)) (ket x) C ψ
    by (cases x  S) (auto simp add: * ccspan_superset' Proj_fixes_image)
  also have  = ket x C (Proj (ccspan (ket ` S)) *V ψ)
    by (simp add: adj_Proj flip: cinner_adj_left)
  finally show ket x C trunc_ell2 S ψ = ket x C (Proj (ccspan (ket ` S)) *V ψ) .
qed


lemma unitary_between_bij_betw:
  assumes is_onb A is_onb B
  shows bij_betw ((*V) (unitary_between A B)) A B
  using bij_between_bases_bij[OF assms]
  by (rule bij_betw_cong[THEN iffD1, rotated])
     (simp add: assms(1) assms(2) unitary_between_apply)

lemma tendsto_finite_subsets_at_top_image:
  assumes inj_on g X
  shows (f  x) (finite_subsets_at_top (g ` X))  ((λS. f (g ` S))  x) (finite_subsets_at_top X)
  by (simp add: filterlim_def assms o_def
      flip: filtermap_image_finite_subsets_at_top filtermap_compose)

(* Should be in Complex_Bounded_Linear_Functions but uses HS2Ell2 *)
lemma Proj_onb_limit:
  shows is_onb A  ((λS. Proj (ccspan S) ψ)  ψ) (finite_subsets_at_top A)
proof -
  have main: ((λS. Proj (ccspan S) ψ)  ψ) (finite_subsets_at_top A) if is_onb A
    for ψ :: 'b::{chilbert_space,not_singleton} and A
  proof -
    define U where U = unitary_between (ell2_to_hilbert* ` A) (range ket)
    have [simp]: unitary U
      by (simp add: U_def that unitary_between_unitary unitary_image_onb)
    have lim1: ((λS. trunc_ell2 S (U *V ell2_to_hilbert* *V ψ))  U *V ell2_to_hilbert* *V ψ) (finite_subsets_at_top UNIV)
      by (rule trunc_ell2_lim_at_UNIV)
    have lim2: ((λS. ell2_to_hilbert *V U* *V trunc_ell2 S (U *V ell2_to_hilbert* *V ψ))  ell2_to_hilbert *V U* *V U *V ell2_to_hilbert* *V ψ) (finite_subsets_at_top UNIV)
      by (intro cblinfun.tendsto lim1) auto
    have *: ell2_to_hilbert *V U* *V trunc_ell2 S (U *V ell2_to_hilbert* *V ψ) 
            = Proj (ccspan ((ell2_to_hilbert o U* o ket) ` S)) ψ (is ?lhs = ?rhs) for S
    proof -
      have ?lhs = (sandwich ell2_to_hilbert *V sandwich (U*) *V Proj (ccspan (ket ` S))) *V ψ
        by (simp add: trunc_ell2_as_Proj sandwich_apply)
      also have  = Proj (ell2_to_hilbert *S U* *S ccspan (ket ` S)) *V ψ
        by (simp add: Proj_sandwich)
      also have  = Proj (ccspan (ell2_to_hilbert ` U* ` ket ` S)) *V ψ
        by (simp add: cblinfun_image_ccspan)
      also have  = ?rhs
        by (simp add: image_comp)
      finally show ?thesis
        by -
    qed
    have **: ell2_to_hilbert *V U* *V U *V ell2_to_hilbert* *V ψ = ψ
      by (simp add: lift_cblinfun_comp[OF unitaryD1] lift_cblinfun_comp[OF unitaryD2])
    have ***: range (ell2_to_hilbert o U* o ket) = A (is ?lhs = _)
    proof -
      have bij_betw U (ell2_to_hilbert* ` A) (range ket)
        by (auto intro!: unitary_between_bij_betw that unitary_image_onb simp add: U_def)
      then have bijUadj: bij_betw (U*) (range ket) (ell2_to_hilbert* ` A)
        by (metis unitary U bij_betw_imp_surj_on inj_imp_bij_betw_inv unitary_adj_inv unitary_inj)
      have ?lhs = ell2_to_hilbert ` U* ` range ket
        by (simp add: image_comp)
      also from this and bijUadj have  = ell2_to_hilbert ` (ell2_to_hilbert* ` A)
        by (metis bij_betw_imp_surj_on)
      also have  = A
        by (metis image_inv_f_f unitary_adj unitary_adj_inv unitary_ell2_to_hilbert unitary_inj)
      finally show ?thesis
        by -
    qed
    from lim2 have lim3: ((λS. Proj (ccspan ((ell2_to_hilbert o U* o ket) ` S)) ψ)  ψ) (finite_subsets_at_top UNIV)
      unfolding * ** by -
    then have lim4: ((λS. Proj (ccspan S) ψ)  ψ) (finite_subsets_at_top (range (ell2_to_hilbert o U* o ket)))
      by (rule tendsto_finite_subsets_at_top_image[THEN iffD2, rotated])
         (intro inj_compose unitary_inj unitary_ell2_to_hilbert unitary_adj[THEN iffD2] unitary U inj_ket)
    then show ?thesis
      unfolding *** by -
  qed
  assume is_onb A
  show ?thesis
  proof (cases class.not_singleton TYPE('a))
    case True
    show ?thesis
      using chilbert_space_class.chilbert_space_axioms True is_onb A
      by (rule main[internalize_sort' 'b2])
  next
    case False
    then have ψ = 0
      by (rule not_not_singleton_zero)
    then show ?thesis
      by simp
  qed
qed

lemma is_ortho_setD:
  assumes "is_ortho_set S" "x  S" "y  S" "x  y"
  shows   "x C y = 0"
  using assms unfolding is_ortho_set_def by blast

lemma finite_rank_dense_compact:
  fixes A :: 'a::chilbert_space set and B :: 'b::chilbert_space set
  assumes is_onb A and is_onb B
  shows closure (cspan ((λ(ξ,η). butterfly ξ η) ` (A × B))) = Collect compact_op
proof (rule Set.equalityI)
  show closure (cspan ((λ(ξ,η). butterfly ξ η) ` (A × B)))  Collect compact_op
  proof -
    have closure (cspan ((λ(ξ,η). butterfly ξ η) ` (A × B)))  closure (Collect finite_rank)
    proof (rule closure_mono; safe)
      fix x assume "x  cspan ((λ(ξ, η). butterfly ξ η) ` (A × B))"
      thus "finite_rank x"
        by (induction rule: complex_vector.span_induct_alt) auto
    qed
    also have  = Collect compact_op
      by (simp add: Set.set_eqI compact_op_finite_rank)
    finally show ?thesis
      by -
  qed
  show Collect compact_op  closure (cspan ((λ(ξ,η). butterfly ξ η) ` (A × B)))
  proof -
    have Collect (compact_op :: 'bCL'a  _) = closure (cspan (Collect rank1))
      by (simp add: compact_op_finite_rank[abs_def] finite_rank_def[abs_def])
    also have   closure (cspan (closure (cspan ((λ(ξ,η). butterfly ξ η) ` (A × B)))))
    proof (rule closure_mono, rule complex_vector.span_mono, rule subsetI)
      fix x :: 'b CL 'a assume x  Collect rank1
      then obtain a b where xab: x = butterfly a b
        using rank1_iff_butterfly by fastforce
      define f where f F G = butterfly (Proj (ccspan F) a) (Proj (ccspan G) b) for F G
      have lim: (case_prod f  x) (finite_subsets_at_top A ×F finite_subsets_at_top B)
      proof (rule tendstoI, subst dist_norm)
        fix e :: real assume e > 0
        define d where d = (if norm a = 0  norm b = 0 then 1 
                                  else e / (max (norm a) (norm b)) / 4)
        have [simp]: "d > 0"
          unfolding d_def using e > 0
          by (auto intro!: divide_pos_pos simp: less_max_iff_disj)
        have d: norm a * d + norm a * d + norm b * d < e
        proof -
          have x * d  e / 4 if x: "x  {norm a, norm b}" for x
          proof (cases "x = 0")
            case False
            have d: "d = e / (max (norm a) (norm b)) / 4"
              using False x by (auto simp: d_def)
            have "d  e / x / 4"
              unfolding d by (intro divide_left_mono divide_right_mono)
                             (use x d > 0 e > 0 False in auto simp: less_max_iff_disj)
            thus ?thesis
              using False x by (auto simp: field_simps)
          qed (use e > 0 in auto)
          hence "norm a * d  e / 4" "norm b * d  e / 4"
            by blast+
          hence norm a * d + norm a * d + norm b * d  3 * e / 4
            by linarith
          also have  < e
            by (simp add: 0 < e)
          finally show ?thesis .
        qed
        from Proj_onb_limit[where ψ=a, OF assms(1)]
        have F F in finite_subsets_at_top A. norm (Proj (ccspan F) a - a) < d
          by (metis Lim_null 0 < d order_tendstoD(2) tendsto_norm_zero_iff)
        moreover from Proj_onb_limit[where ψ=b, OF assms(2)]
        have F G in finite_subsets_at_top B. norm (Proj (ccspan G) b - b) < d
          by (metis Lim_null 0 < d order_tendstoD(2) tendsto_norm_zero_iff)
        ultimately have FG_close: F (F,G) in finite_subsets_at_top A ×F finite_subsets_at_top B. 
              norm (Proj (ccspan F) a - a) < d  norm (Proj (ccspan G) b - b) < d
          unfolding case_prod_beta
          by (rule eventually_prodI)
        have fFG_dist: norm (f F G - x) < e 
          if norm (Proj (ccspan F) a - a) < d and norm (Proj (ccspan G) b - b) < d
            and F  A and G  B for F G
        proof -
          have a_split: a = Proj (ccspan F) *V a + Proj (ccspan (A-F)) *V a
          proof -
            have A: "is_ortho_set A" "ccspan A = "
              using assms unfolding is_onb_def by auto
            have "Proj (ccspan (F  A)) = Proj (ccspan F) + Proj (ccspan (A-F))"
              by (subst Proj_orthog_ccspan_union [symmetric])
                 (use that in auto intro!: is_ortho_setD[OF A(1)])
            also have "F  A = A"
              using that by blast
            finally show ?thesis
              using A(2) by (simp flip: cblinfun.add_left)
          qed

          have b_split: b = Proj (ccspan G) *V b + Proj (ccspan (B-G)) *V b
          proof -
            have B: "is_ortho_set B" "ccspan B = "
              using assms unfolding is_onb_def by auto
            have "Proj (ccspan (G  B)) = Proj (ccspan G) + Proj (ccspan (B-G))"
              by (subst Proj_orthog_ccspan_union [symmetric])
                 (use that in auto intro!: is_ortho_setD[OF B(1)])
            also have "G  B = B"
              using that by blast
            finally show ?thesis
              using B(2) by (simp flip: cblinfun.add_left)
          qed

          have n1: norm (f F (B-G))  norm a * d for F
          proof -
            have norm (f F (B-G))  norm a * norm (Proj (ccspan (B-G)) b)
              by (auto intro!: mult_right_mono is_Proj_reduces_norm simp add: f_def norm_butterfly)
            also have   norm a * norm (Proj (ccspan G) b - b)
              by (metis add_diff_cancel_left' b_split less_eq_real_def norm_minus_commute)
            also have   norm a * d
              by (meson less_eq_real_def mult_left_mono norm_ge_zero that(2))
            finally show ?thesis
              by -
          qed
          have n2: norm (f (A-F) G)  norm b * d for G
          proof -
            have norm (f (A-F) G)  norm b * norm (Proj (ccspan (A-F)) a)
              by (auto intro!: mult_right_mono is_Proj_reduces_norm simp add: f_def norm_butterfly mult.commute)
            also have   norm b * norm (Proj (ccspan F) a - a)
              by (smt (verit, best) a_split add_diff_cancel_left' minus_diff_eq norm_minus_cancel)
            also have   norm b * d
              by (meson less_eq_real_def mult_left_mono norm_ge_zero that(1))
            finally show ?thesis
              by -
          qed
          have norm (f F G - x) = norm (- f F (B-G) - f (A-F) (B-G) - f (A-F) G)
            unfolding xab
            by (subst a_split, subst b_split)
               (simp add: f_def butterfly_add_right butterfly_add_left)
          also have   norm (f F (B-G)) + norm (f (A-F) (B-G)) + norm (f (A-F) G)
            by (smt (verit, best) norm_minus_cancel norm_triangle_ineq4)
          also have   norm a * d + norm a * d + norm b * d
            using n1 n2
            by (meson add_mono_thms_linordered_semiring(1))
          also have  < e
            by (fact d)
          finally show ?thesis
            by -
        qed
        have "F (F, G) in finite_subsets_at_top A ×F finite_subsets_at_top B.
                (finite F  F  A)  finite G  G  B"
          unfolding case_prod_unfold by (intro eventually_prodI) auto
        thus F FG in finite_subsets_at_top A ×F finite_subsets_at_top B. 
                norm ((case FG of (F, G)  f F G) - x) < e
          using FG_close by eventually_elim (use fFG_dist in auto)
      qed
      have nontriv: finite_subsets_at_top A ×F finite_subsets_at_top B  
        by (simp add: prod_filter_eq_bot)
      have inside: F x in finite_subsets_at_top A ×F finite_subsets_at_top B. 
              case_prod f x  cspan ((λ(ξ,η). butterfly ξ η) ` (A × B))
      proof (rule eventually_mp[where P=λ(F,G). finite F  finite G])
        show F (F,G) in finite_subsets_at_top A ×F finite_subsets_at_top B. finite F  finite G
          by (smt (verit) case_prod_conv eventually_finite_subsets_at_top_weakI eventually_prod_filter)
        have f_in_span: f F G  cspan ((λ(ξ,η). butterfly ξ η) ` (A × B)) if [simp]: finite F finite G and F  A G  B for F G
        proof -
          have Proj (ccspan F) a  cspan F
            by (metis Proj_range cblinfun_apply_in_image ccspan_finite that(1))
          then obtain r where ProjFsum: Proj (ccspan F) a = (xF. r x *C x)
            using complex_vector.span_finite[OF finite F] by auto
          have Proj (ccspan G) b  cspan G
            by (metis Proj_range cblinfun_apply_in_image ccspan_finite that(2))
          then obtain s where ProjGsum: Proj (ccspan G) b = (xG. s x *C x)
            using complex_vector.span_finite[OF finite G] by auto
          have butterfly ξ η  (λ(ξ, η). butterfly ξ η) ` (A × B)
            if η  G and ξ  F for η ξ
            using F  A G  B that by auto
          then show ?thesis
            by (auto intro!: complex_vector.span_sum complex_vector.span_scale
                complex_vector.span_base[where a=butterfly _ _]
                simp add: f_def ProjFsum ProjGsum butterfly_sum_left butterfly_sum_right)
        qed
        have "F (F, G) in finite_subsets_at_top A ×F finite_subsets_at_top B.
                (finite F  F  A)  finite G  G  B"
          unfolding case_prod_unfold by (intro eventually_prodI) auto
        thus F x in finite_subsets_at_top A ×F finite_subsets_at_top B.
                      (case x of (F, G)  finite F  finite G)  (case x of (F, G)  f F G)  cspan ((λ(ξ, η). butterfly ξ η) ` (A × B))
          by eventually_elim (auto intro!: f_in_span)
      qed
      show x  closure (cspan ((λ(ξ, η). butterfly ξ η) ` (A × B)))
        using lim nontriv inside by (rule limit_in_closure)
    qed
    also have  = closure (cspan ((λ(ξ,η). butterfly ξ η) ` (A × B)))
      by (simp add: complex_vector.span_eq_iff[THEN iffD2])
    finally show ?thesis
      by -
  qed
qed

lemma compact_op_comp_left: compact_op (a oCL b) if compact_op a
  for a b :: _::chilbert_space CL _::chilbert_space
proof -
  from that have a  closure (Collect finite_rank)
  using compact_op_finite_rank by blast
  then have a oCL b  (λa. a oCL b) ` closure (Collect finite_rank)
    by blast
  also have   closure ((λa. a oCL b) ` Collect finite_rank)
    by (auto intro!: closure_bounded_linear_image_subset bounded_clinear.bounded_linear bounded_clinear_cblinfun_compose_left)
  also have   closure (Collect finite_rank)
    by (auto intro!: closure_mono finite_rank_comp_left)
  finally show compact_op (a oCL b)
    using compact_op_finite_rank by blast
qed


lemma compact_op_eigenspace_finite_dim:
  fixes a :: 'a CL 'a::chilbert_space
  assumes compact_op a
  assumes e  0
  shows finite_dim_ccsubspace (eigenspace e a)
proof -
  define S where S = space_as_set (eigenspace e a)
  obtain B where ccspan B = eigenspace e a and is_ortho_set B
    and norm_B: x  B  norm x = 1 for x
    using orthonormal_subspace_basis_exists[where S={} and V=eigenspace e a]
    by (auto simp: S_def)
  then have span_BS: closure (cspan B) = S
    by (metis S_def ccspan.rep_eq)
  have finite B
  proof (rule ccontr)
    assume infinite B
    then obtain b :: nat  'a where range_b: range b  B and inj b
      by (meson infinite_countable_subset)
    define f where f n = a (b n) for n
    have range_f: range f  closure (a ` cball 0 1)
      using norm_B range_b
      by (auto intro!: closure_subset[THEN subsetD] imageI simp: f_def)
    from compact_op a have compact: compact (closure (a ` cball 0 1))
      using compact_op_def2 by blast
    obtain l r where strict_mono r and fr_lim: (f o r)  l
      using range_f compact[unfolded compact_def, rule_format, of f]
      by fast
    define d :: real where d = cmod e * sqrt 2
    from e  0 have d > 0
      by (auto intro!: Rings.linordered_semiring_strict_class.mult_pos_pos simp: d_def)
    have aux: nN. P n if P (Suc N) for P N
      using Suc_n_not_le_n nat_le_linear that by blast
    have dist (f (r n)) (f (r (Suc n))) = d for n
    proof -
      have ortho: is_orthogonal (b (r n)) (b (r (Suc n)))
      proof -
        have b (r n)  b (r (Suc n))
          by (metis Suc_n_not_n inj b strict_mono r injD strict_mono_eq)
        moreover from range_b have b (r n)  B and b (r (Suc n))  B
          by fast+
        ultimately show ?thesis
          using is_ortho_set B 
          by (auto intro!: simp: is_ortho_set_def)
      qed
      have normb: norm (b n) = 1 for n
        by (metis inj b image_subset_iff inj_image_mem_iff norm_B range_b range_eqI)
      have f (r n) = e *C b (r n) for n
      proof -
        from range_b span_BS
        have b (r n)  S
          using complex_vector.span_superset closure_subset
          by (blast dest: range_subsetD[where i = b n])
        then show ?thesis
          by (auto intro!: dest!: eigenspace_memberD simp: S_def f_def)
      qed
      then have (dist (f (r n)) (f (r (Suc n))))2 = (cmod e * dist (b (r n)) (b (r (Suc n))))2
        by (simp add: dist_norm flip: scaleC_diff_right)
      also from ortho have  = (cmod e * sqrt 2)2
        by (simp add: dist_norm polar_identity_minus power_mult_distrib normb)
      finally show ?thesis
        by (simp add: d_def)
    qed
    with d > 0 have ¬ Cauchy (f o r)
      by (auto intro!: exI[of _ d/2] aux
          simp: Cauchy_altdef2 dist_commute simp del: less_divide_eq_numeral1)
    with fr_lim show False
      using LIMSEQ_imp_Cauchy by blast
  qed
  with span_BS show ?thesis
    using S_def cspan_finite_dim finite_dim_ccsubspace.rep_eq by fastforce
qed

lemma eigenvalue_in_the_limit_compact_op:
  ― ‹citeconway2013course, Proposition II.4.14›
  assumes compact_op T
  assumes l  0
  assumes normh: n. norm (h n) = 1
  assumes Tl_lim: (λn. (T - l *C id_cblinfun) (h n))  0
  shows l  eigenvalues T
proof -
  from assms(1)
  have compact_Tball: compact (closure (T ` cball 0 1))
    using compact_op_def2 by blast
  have T (h n)  closure (T ` cball 0 1) for n
    by (smt (z3) assms(3) closure_subset image_subset_iff mem_cball_0)
  then obtain n f where lim_Thn: (λk. T (h (n k)))  f and strict_mono n
    using compact_Tball[unfolded compact_def, rule_format, where f=T o h, unfolded o_def]
    by fast
  have lThk_lim: (λk. (l *C id_cblinfun - T) (h (n k)))  0
  proof -
    have (λn. (l *C id_cblinfun - T) (h n))  0
      using Tl_lim[THEN tendsto_minus]
      by (simp add: cblinfun.diff_left)
    with strict_mono n show ?thesis
      by (rule LIMSEQ_subseq_LIMSEQ[unfolded o_def, rotated])
  qed
  have h (n k) = inverse l *C ((l *C id_cblinfun - T) (h (n k)) + T (h (n k))) for k
    by (metis assms(2) cblinfun.real.add_left cblinfun.scaleC_left diff_add_cancel divideC_field_splits_simps_1(5) id_cblinfun.rep_eq scaleC_zero_right)
  moreover have   inverse l *C (0 + f)
    by (intro tendsto_intros lThk_lim lim_Thn)
  ultimately have lim_hn: (λk. h (n k))  inverse l *C f
    by simp
  have f  0
  proof -
    from lim_hn have (λk. norm (h (n k)))  norm (inverse l *C f)
      by (rule isCont_tendsto_compose[unfolded o_def, rotated]) fastforce
    moreover have (λ_. 1)  1
      by simp
    ultimately have norm (inverse l *C f) = 1
      unfolding normh
      using LIMSEQ_unique by blast
    then show f  0
      by force
  qed
  from lim_hn have (λk. T (h (n k)))  T (inverse l *C f)
    by (rule isCont_tendsto_compose[rotated]) force
  with lim_Thn have f = T (inverse l *C f)
    using LIMSEQ_unique by blast
  with l  0 have l *C f = T f
    by (metis cblinfun.scaleC_right divideC_field_simps(2))
  with f  0 show l  eigenvalues T
    by (auto intro!: eigenvaluesI[where h=f])
qed


lemma norm_is_eigenvalue:
  ― ‹citeconway2013course, Proposition II.5.9›
  fixes a :: 'a CL 'a::{not_singleton, chilbert_space}
  assumes compact_op a
  assumes selfadjoint a
  shows norm a  eigenvalues a  - norm a  eigenvalues a
proof -
  wlog a  0
    using negation by auto
  obtain h e where h_lim: (λi. h i C a (h i))  e and normh: norm (h i) = 1 
    and norme: cmod e = norm a for i
  proof atomize_elim
    have sgn_cmod: sgn x * cmod x = x for x
      by (simp add: complex_of_real_cmod sgn_mult_abs)
    from cblinfun_norm_is_Sup_cinner[OF selfadjoint a]
    obtain f where range_f: range f  ((λψ. cmod (ψ C (a *V ψ))) ` {ψ. norm ψ = 1})
      and f_lim: f  norm a
      by (atomize_elim, rule is_Sup_imp_ex_tendsto) (auto simp: ex_norm1_not_singleton)
    obtain h0 where normh0: norm (h0 i) = 1 and f_h0: f i = cmod (h0 i C a (h0 i)) for i
      by (atomize_elim, rule choice2) (use range_f in auto)
    from f_h0 f_lim have h0lim_cmod: (λi. cmod (h0 i C a (h0 i)))  norm a
      by presburger
    have sgn_sphere: sgn (h0 i C a (h0 i))  insert 0 (sphere 0 1) for i
      using normh0 by (auto intro!: left_inverse simp: sgn_div_norm)
    have compact: compact (insert 0 (sphere (0::complex) 1))
      by simp
    obtain r l where strict_mono r and l_sphere: l  insert 0 (sphere 0 1)
      and h0lim_sgn: ((λi. sgn (h0 i C a (h0 i)))  r)  l
      using compact[unfolded compact_def, rule_format, OF sgn_sphere]
      by fast
    define h and e where h i = h0 (r i) and e = l * norm a for i
    have hlim_cmod: (λi. cmod (h i C a (h i)))  norm a
      using LIMSEQ_subseq_LIMSEQ[OF h0lim_cmod strict_mono r]  
      unfolding h_def o_def by auto
    with h0lim_sgn have (λi. sgn (h i C a (h i)) * cmod (h i C a (h i)))  e
      by (auto intro!: tendsto_mult tendsto_of_real simp: o_def h_def e_def)
    then have hlim: (λi. h i C a (h i))  e
      by (simp add: sgn_cmod)
    have e  0
    proof (rule ccontr, unfold not_not)
      assume e = 0
      from hlim have (λi. cmod (h i C a (h i)))  cmod e
        by (rule tendsto_compose[where g=cmod, rotated])
           (smt (verit, del_insts) e = 0 diff_zero dist_norm metric_LIM_imp_LIM 
              norm_ge_zero norm_zero real_norm_def tendsto_ident_at)
      with e = 0 hlim_cmod have norm a = 0
        using LIMSEQ_unique by fastforce
      with a  0 show False
        by simp
    qed
    then have norme: norm e = norm a
      using l_sphere by (simp add: e_def norm_mult)
    show h e. (λi. h i C (a *V h i))  e  (i. norm (h i) = 1)  cmod e = norm a
      using norme normh0 hlim
      by (auto intro!: exI[of _ h] exI[of _ e] simp: h_def)
  qed
  have e  
  proof -
    from h_lim[THEN tendsto_Im]
    have *: (λi. Im (h i C a (h i)))  Im e
      by -
    have **: Im (h i C a (h i)) = 0 for i
      using assms(2) selfadjoint_def cinner_hermitian_real complex_is_Real_iff by auto
    have Im e = 0
      using _ * by (rule tendsto_unique) (use ** in auto)
    then show ?thesis
      using complex_is_Real_iff by presburger
  qed
  define e' where e' = Re e
  with e   have ee': e = of_real e'
    by simp
  have e'  eigenvalues a
  proof -
    have [trans]: f  0 if x. f x  g x and g  0 and x. f x  0 for f g :: nat  real
      by (rule real_tendsto_sandwich[where h=g and f=λ_. 0]) (use that in auto)
    have [simp]: "a* = a"
      using assms(2) by (simp add: selfadjoint_def)
    have [simp]: "Re (h x C h x) = 1" for x
      using normh[of x] by (simp flip: power2_norm_eq_cinner')
    have (norm ((a - e' *R id_cblinfun) (h n)))2 = (norm (a (h n)))2 - 2 * e' * Re (h n C a (h n)) + e'2 for n
      by (simp add: power2_norm_eq_cinner' algebra_simps cblinfun.cbilinear_simps 
                    cblinfun.scaleR_left power2_eq_square[of e'] flip: cinner_adj_right)
    also have n  e'2 - 2 * e' * Re (h n C a (h n)) + e'2 for n
    proof -
      from norme have e'2 = (norm a)2
        by (auto simp: ee' power2_eq_iff abs_if split: if_splits)
      then have (norm (a *V h n))2  e'2
        using norm_cblinfun[of a "h n"] by (simp add: normh)
      then show ?thesis
        by auto
    qed
    also have   0
      apply (subst asm_rl[of (λn. e'2 - 2 * e' * Re (h n C a (h n)) + e'2) = (λn. 2 * e' * (e' - Re (h n C (a *V h n))))])
      subgoal
        by (auto simp: fun_eq_iff right_diff_distrib power2_eq_square)[1]
      subgoal
        using h_lim[THEN tendsto_Re]
        by (auto intro!: tendsto_mult_right_zero tendsto_diff_const_left_rewrite simp: ee')
      done
    finally have (λn. (a - e' *R id_cblinfun) (h n))  0
      by (simp add: tendsto_norm_zero_iff)
    then show e'  eigenvalues a
      unfolding scaleR_scaleC
      by (rule eigenvalue_in_the_limit_compact_op[rotated -1])
         (use a  0 norme in auto intro!: normh simp: assms ee')
  qed
  from e   norme
  have e = norm a  e = - norm a
    by (smt (verit, best) in_Reals_norm of_real_Re)
  with e'  eigenvalues a show ?thesis
    using ee' by presburger
qed

lemma
  fixes a :: 'a CL 'a::{not_singleton, chilbert_space}
  assumes compact_op a
  assumes selfadjoint a
  shows largest_eigenvalue_norm_aux: largest_eigenvalue a  {norm a, - norm a}
    and largest_eigenvalue_ex: largest_eigenvalue a  eigenvalues a
proof -
  define l where l = (SOME x. x  eigenvalues a  (y  eigenvalues a. cmod x  cmod y))
  from norm_is_eigenvalue[OF assms]
  obtain e where e  {of_real (norm a), - of_real (norm a)} and e  eigenvalues a
    by auto
  then have norme: norm e = norm a
    by auto
  have e  eigenvalues a  (yeigenvalues a. cmod y  cmod e) (is ?P e)
    by (auto intro!: e  eigenvalues a simp: eigenvalue_norm_bound norme)
  then have *: l  eigenvalues a  (yeigenvalues a. cmod y  cmod l)
    unfolding l_def largest_eigenvalue_def by (rule someI)
  then have l_def': l = largest_eigenvalue a
    by (metis (mono_tags, lifting) l_def largest_eigenvalue_def) 
  from * have l  eigenvalues a
    by (simp add: l_def)
  then show largest_eigenvalue a  eigenvalues a
    by (simp add: l_def')
  have norm l  norm a
    using * norme e  eigenvalues a by auto
  moreover have norm l  norm a
    using "*" eigenvalue_norm_bound by blast
  ultimately have norm l = norm a
    by linarith
  moreover have l  
    using l  eigenvalues a assms(2) eigenvalue_selfadj_real by blast
  ultimately have l  {norm a, - norm a}
    by (smt (verit, ccfv_SIG) in_Reals_norm insertCI l_def of_real_Re)
  then show largest_eigenvalue a  {norm a, - norm a}
    by (simp add: l_def')
qed

lemma largest_eigenvalue_norm:
  fixes a :: 'a CL 'a::chilbert_space
  assumes compact_op a
  assumes selfadjoint a
  shows largest_eigenvalue a  {norm a, - norm a}
proof (cases class.not_singleton TYPE('a))
  case True
  show ?thesis
    using chilbert_space_class.chilbert_space_axioms True assms 
    by (rule largest_eigenvalue_norm_aux[internalize_sort' 'a])
next
  case False
  then have a = 0
    by (rule not_not_singleton_cblinfun_zero)
  then show ?thesis
    by simp
qed

hide_fact largest_eigenvalue_norm_aux

lemma cmod_largest_eigenvalue:
  fixes a :: 'a CL 'a::chilbert_space
  assumes compact_op a
  assumes selfadjoint a
  shows cmod (largest_eigenvalue a) = norm a
  using largest_eigenvalue_norm[OF assms] by auto

lemma compact_op_comp_right: compact_op (a oCL b) if compact_op b
  for a b :: _::chilbert_space CL _::chilbert_space
proof -
  from that have b  closure (Collect finite_rank)
  using compact_op_finite_rank by blast
  then have a oCL b  cblinfun_compose a ` closure (Collect finite_rank)
    by blast
  also have   closure (cblinfun_compose a ` Collect finite_rank)
    by (auto intro!: closure_bounded_linear_image_subset bounded_clinear.bounded_linear bounded_clinear_cblinfun_compose_right)
  also have   closure (Collect finite_rank)
    by (auto intro!: closure_mono finite_rank_comp_right)
  finally show compact_op (a oCL b)
    using compact_op_finite_rank by blast
qed


end