Theory Complex_Bounded_Linear_Function

section Complex_Bounded_Linear_Function› -- Complex bounded linear functions (bounded operators)›

(*
Authors:

  Dominique Unruh, University of Tartu, unruh@ut.ee
  Jose Manuel Rodriguez Caballero, University of Tartu, jose.manuel.rodriguez.caballero@ut.ee

*)

theory Complex_Bounded_Linear_Function
  imports
    "HOL-Types_To_Sets.Types_To_Sets"
    Banach_Steinhaus.Banach_Steinhaus
    Complex_Inner_Product
    One_Dimensional_Spaces
    Complex_Bounded_Linear_Function0
    "HOL-Library.Function_Algebras"
begin

unbundle lattice_syntax

subsection ‹Misc basic facts and declarations›

notation cblinfun_apply (infixr "*V" 70)

lemma id_cblinfun_apply[simp]: "id_cblinfun *V ψ = ψ"
  by simp

lemma apply_id_cblinfun[simp]: (*V) id_cblinfun = id
  by auto

lemma isCont_cblinfun_apply[simp]: "isCont ((*V) A) ψ"
  by transfer (simp add: clinear_continuous_at)

declare cblinfun.scaleC_left[simp]

lemma cblinfun_apply_clinear[simp]: clinear (cblinfun_apply A)
  using bounded_clinear.axioms(1) cblinfun_apply by blast

lemma cblinfun_cinner_eqI:
  fixes A B :: 'a::chilbert_space CL 'a
  assumes ψ. norm ψ = 1  cinner ψ (A *V ψ) = cinner ψ (B *V ψ)
  shows A = B
proof -
  define C where C = A - B
  have C0[simp]: cinner ψ (C ψ) = 0 for ψ
    apply (cases ψ = 0)
    using assms[of sgn ψ]
    by (simp_all add: C_def norm_sgn sgn_div_norm cblinfun.scaleR_right assms cblinfun.diff_left cinner_diff_right)
  { fix f g α
    have 0 = cinner (f + α *C g) (C *V (f + α *C g))
      by (simp add: cinner_diff_right minus_cblinfun.rep_eq)
    also have  = α *C cinner f (C g) + cnj α *C cinner g (C f)
      by (smt (z3) C0 add.commute add.right_neutral cblinfun.add_right cblinfun.scaleC_right cblinfun_cinner_right.rep_eq cinner_add_left cinner_scaleC_left complex_scaleC_def)
    finally have α *C cinner f (C g) = - cnj α *C cinner g (C f)
      by (simp add: eq_neg_iff_add_eq_0)
  }
  then have cinner f (C g) = 0 for f g
    by (metis complex_cnj_i complex_cnj_one complex_vector.scale_cancel_right complex_vector.scale_left_imp_eq equation_minus_iff i_squared mult_eq_0_iff one_neq_neg_one)
  then have C g = 0 for g
    using cinner_eq_zero_iff by blast
  then have C = 0
    by (simp add: cblinfun_eqI)
  then show A = B
    using C_def by auto
qed

lemma id_cblinfun_not_0[simp]: (id_cblinfun :: 'a::{complex_normed_vector, not_singleton} CL _)  0
  by (metis (full_types) Extra_General.UNIV_not_singleton cblinfun.zero_left cblinfun_id_cblinfun_apply ex_norm1 norm_zero one_neq_zero)

lemma cblinfun_norm_geqI:
  assumes norm (f *V x) / norm x  K
  shows norm f  K
  using assms by transfer (smt (z3) bounded_clinear.bounded_linear le_onorm)

(* This lemma is proven in Complex_Bounded_Linear_Function0 but we add the [simp]
   only here because we try to keep Complex_Bounded_Linear_Function0 as close to
   Bounded_Linear_Function as possible. *)
declare scaleC_conv_of_complex[simp]

lemma cblinfun_eq_0_on_span:
  fixes S::'a::complex_normed_vector set
  assumes "x  cspan S"
    and "s. sS  F *V s = 0"
  shows F *V x = 0
  using bounded_clinear.axioms(1) cblinfun_apply assms complex_vector.linear_eq_0_on_span
  by blast

lemma cblinfun_eq_on_span:
  fixes S::'a::complex_normed_vector set
  assumes "x  cspan S"
    and "s. sS  F *V s = G *V s"
  shows F *V x = G *V x
  using bounded_clinear.axioms(1) cblinfun_apply assms complex_vector.linear_eq_on_span
  by blast

lemma cblinfun_eq_0_on_UNIV_span:
  fixes basis::'a::complex_normed_vector set
  assumes "cspan basis = UNIV"
    and "s. sbasis  F *V s = 0"
  shows F = 0
  by (metis cblinfun_eq_0_on_span UNIV_I assms cblinfun.zero_left cblinfun_eqI)

lemma cblinfun_eq_on_UNIV_span:
  fixes basis::"'a::complex_normed_vector set" and φ::"'a  'b::complex_normed_vector"
  assumes "cspan basis = UNIV"
    and "s. sbasis  F *V s = G *V s"
  shows F = G
  by (metis (no_types) assms cblinfun_eqI cblinfun_eq_on_span iso_tuple_UNIV_I)

lemma cblinfun_eq_on_canonical_basis:
  fixes f g::"'a::{basis_enum,complex_normed_vector} CL 'b::complex_normed_vector"
  defines "basis == set (canonical_basis::'a list)"
  assumes "u. u  basis  f *V u = g *V u"
  shows  "f = g"
  using assms cblinfun_eq_on_UNIV_span is_generator_set by blast

lemma cblinfun_eq_0_on_canonical_basis:
  fixes f ::"'a::{basis_enum,complex_normed_vector} CL 'b::complex_normed_vector"
  defines "basis == set (canonical_basis::'a list)"
  assumes "u. u  basis  f *V u = 0"
  shows  "f = 0"
  by (simp add: assms cblinfun_eq_on_canonical_basis)

lemma cinner_canonical_basis_eq_0:
  defines "basisA == set (canonical_basis::'a::onb_enum list)"
    and   "basisB == set (canonical_basis::'b::onb_enum list)"
  assumes "u v. ubasisA  vbasisB  is_orthogonal v (F *V u)"
  shows "F = 0"
proof-
  have "F *V u = 0"
    if "ubasisA" for u
  proof-
    have "v. vbasisB  is_orthogonal v (F *V u)"
      by (simp add: assms(3) that)
    moreover have "(v. vbasisB  is_orthogonal v x)  x = 0"
      for x
    proof-
      assume r1: "v. vbasisB  is_orthogonal v x"
      have "is_orthogonal v x" for v
      proof-
        have "cspan basisB = UNIV"
          using basisB_def is_generator_set  by auto
        hence "v  cspan basisB"
          by (smt iso_tuple_UNIV_I)
        hence "t s. v = (at. s a *C a)  finite t  t  basisB"
          using complex_vector.span_explicit
          by (smt mem_Collect_eq)
        then obtain t s where b1: "v = (at. s a *C a)" and b2: "finite t" and b3: "t  basisB"
          by blast
        have "v C x = (at. s a *C a) C x"
          by (simp add: b1)
        also have " = (at. (s a *C a) C x)"
          using cinner_sum_left by blast
        also have " = (at. cnj (s a) * (a C x))"
          by auto
        also have " = 0"
          using b3 r1 subsetD by force
        finally show ?thesis by simp
      qed
      thus ?thesis
        by (simp add: v. (v C x) = 0 cinner_extensionality)
    qed
    ultimately show ?thesis by simp
  qed
  thus ?thesis
    using basisA_def cblinfun_eq_0_on_canonical_basis by auto
qed

lemma cinner_canonical_basis_eq:
  defines "basisA == set (canonical_basis::'a::onb_enum list)"
    and   "basisB == set (canonical_basis::'b::onb_enum list)"
  assumes "u v. ubasisA  vbasisB  v C (F *V u) = v C (G *V u)"
  shows "F = G"
proof-
  define H where "H = F - G"
  have "u v. ubasisA  vbasisB  is_orthogonal v (H *V u)"
    unfolding H_def
    by (simp add: assms(3) cinner_diff_right minus_cblinfun.rep_eq)
  hence "H = 0"
    by (simp add: basisA_def basisB_def cinner_canonical_basis_eq_0)
  thus ?thesis unfolding H_def by simp
qed

lemma cinner_canonical_basis_eq':
  defines "basisA == set (canonical_basis::'a::onb_enum list)"
    and   "basisB == set (canonical_basis::'b::onb_enum list)"
  assumes "u v. ubasisA  vbasisB  (F *V u) C v = (G *V u) C v"
  shows "F = G"
  using cinner_canonical_basis_eq assms
  by (metis cinner_commute')

lemma not_not_singleton_cblinfun_zero: 
  x = 0 if ¬ class.not_singleton TYPE('a) for x :: 'a::complex_normed_vector CL 'b::complex_normed_vector
  apply (rule cblinfun_eqI)
  apply (subst not_not_singleton_zero[OF that])
  by simp

lemma cblinfun_norm_approx_witness:
  fixes A :: 'a::{not_singleton,complex_normed_vector} CL 'b::complex_normed_vector
  assumes ε > 0
  shows ψ. norm (A *V ψ)  norm A - ε  norm ψ = 1
proof (transfer fixing: ε)
  fix A :: 'a  'b assume [simp]: bounded_clinear A
  have y{norm (A x) |x. norm x = 1}. y >  {norm (A x) |x. norm x = 1} - ε
    apply (rule Sup_real_close)
    using assms by (auto simp: ex_norm1 bounded_clinear.bounded_linear bdd_above_norm_f)
  also have  {norm (A x) |x. norm x = 1} = onorm A
    by (simp add: bounded_clinear.bounded_linear onorm_sphere)
  finally
  show ψ. onorm A - ε  norm (A ψ)  norm ψ = 1
    by force
qed

lemma cblinfun_norm_approx_witness_mult:
  fixes A :: 'a::{not_singleton,complex_normed_vector} CL 'b::complex_normed_vector
  assumes ε < 1
  shows ψ.  norm (A *V ψ)  norm A * ε  norm ψ = 1
proof (cases norm A = 0)
  case True
  then show ?thesis
    by auto (simp add: ex_norm1)
next
  case False
  then have (1 - ε) * norm A > 0
    using assms by fastforce
  then obtain ψ where geq: norm (A *V ψ)  norm A - ((1 - ε) * norm A) and norm ψ = 1
    using cblinfun_norm_approx_witness by blast
  have norm A * ε = norm A - (1 - ε) * norm A
    by (simp add: mult.commute right_diff_distrib')
  also have   norm (A *V ψ)
    by (rule geq)
  finally show ?thesis
    using norm ψ = 1 by auto
qed


lemma cblinfun_norm_approx_witness':
  fixes A :: 'a::complex_normed_vector CL 'b::complex_normed_vector
  assumes ε > 0
  shows ψ. norm (A *V ψ) / norm ψ  norm A - ε
proof (cases class.not_singleton TYPE('a))
  case True
  obtain ψ where norm (A *V ψ)  norm A - ε and norm ψ = 1
    apply atomize_elim
    using complex_normed_vector_axioms True assms
    by (rule cblinfun_norm_approx_witness[internalize_sort' 'a])
  then have norm (A *V ψ) / norm ψ  norm A - ε
    by simp
  then show ?thesis 
    by auto
next
  case False
  show ?thesis
    apply (subst not_not_singleton_cblinfun_zero[OF False])
     apply simp
    apply (subst not_not_singleton_cblinfun_zero[OF False])
    using ε > 0 by simp
qed

lemma cblinfun_to_CARD_1_0[simp]: (A :: _ CL _::CARD_1) = 0
  by (simp add: cblinfun_eqI)

lemma cblinfun_from_CARD_1_0[simp]: (A :: _::CARD_1 CL _) = 0
  using cblinfun_eq_on_UNIV_span by force


lemma cblinfun_cspan_UNIV:
  fixes basis :: ('a::{complex_normed_vector,cfinite_dim} CL 'b::complex_normed_vector) set
    and basisA :: 'a set and basisB :: 'b set
  assumes cspan basisA = UNIV and cspan basisB = UNIV
  assumes basis: a b. abasisA  bbasisB  Fbasis. a'basisA. F *V a' = (if a'=a then b else 0)
  shows cspan basis = UNIV
proof -
  obtain basisA' where basisA'  basisA and cindependent basisA' and cspan basisA' = UNIV
    by (metis assms(1) complex_vector.maximal_independent_subset complex_vector.span_eq top_greatest)
  then have [simp]: finite basisA'
    by (simp add: cindependent_cfinite_dim_finite)
  have basis': a b. abasisA'  bbasisB  Fbasis. a'basisA'. F *V a' = (if a'=a then b else 0)
    using basis basisA'  basisA by fastforce

  obtain F where F: F a b  basis  F a b *V a' = (if a'=a then b else 0)
    if abasisA' bbasisB a'basisA' for a b a'
    apply atomize_elim apply (intro choice allI)
    using basis' by metis
  then have F_apply: F a b *V a' = (if a'=a then b else 0)
    if abasisA' bbasisB a'basisA' for a b a'
    using that by auto
  have F_basis: F a b  basis
    if abasisA' bbasisB for a b
    using that F by auto
  have b_span: Gcspan {F a b|b. bbasisB}. a'basisA'. G *V a' = (if a'=a then b else 0) if abasisA' for a b
  proof -
    from cspan basisB = UNIV
    obtain r t where finite t and t  basisB and b_lincom: b = (at. r a *C a)
      unfolding complex_vector.span_alt by atomize_elim blast
    define G where G = (it. r i *C F a i)
    have G  cspan {F a b|b. bbasisB}
      using finite t t  basisB unfolding G_def
      by (smt (verit) complex_vector.span_scale complex_vector.span_sum complex_vector.span_superset mem_Collect_eq subsetD)
    moreover have G *V a' = (if a'=a then b else 0) if a'basisA' for a'
      using t  basisB abasisA' a'basisA'
      by (auto simp: b_lincom G_def cblinfun.sum_left F_apply intro!: sum.neutral sum.cong)
    ultimately show ?thesis
      by blast
  qed

  have a_span: cspan (abasisA'. cspan {F a b|b. bbasisB}) = UNIV
  proof (intro equalityI subset_UNIV subsetI, rename_tac H)
    fix H
    obtain G where G: G a b  cspan {F a b|b. bbasisB}  G a b *V a' = (if a'=a then b else 0) 
      if abasisA' and a'basisA' for a b a'
      apply atomize_elim apply (intro choice allI)
      using b_span by blast
    then have G_cspan: G a b  cspan {F a b|b. bbasisB} if abasisA' for a b
      using that by auto
    from G have G: G a b *V a' = (if a'=a then b else 0) if abasisA' and a'basisA' for a b a'
      using that by auto
    define H' where H' = (abasisA'. G a (H *V a))
    have H'  cspan (abasisA'. cspan {F a b|b. bbasisB})
      unfolding H'_def using G_cspan
      by (smt (verit, del_insts) UN_iff complex_vector.span_clauses(1) complex_vector.span_sum)
    moreover have H' = H
      using cspan basisA' = UNIV 
      by (auto simp: H'_def cblinfun_eq_on_UNIV_span cblinfun.sum_left G)
    ultimately show H  cspan (abasisA'. cspan {F a b |b. b  basisB})
      by simp
  qed

  moreover have cspan basis  cspan (abasisA'. cspan {F a b|b. bbasisB})
    by (smt (verit) F_basis UN_subset_iff complex_vector.span_base complex_vector.span_minimal complex_vector.subspace_span mem_Collect_eq subsetI)

  ultimately show cspan basis = UNIV
    by auto
qed


instance cblinfun :: ({cfinite_dim,complex_normed_vector}, {cfinite_dim,complex_normed_vector}) cfinite_dim
proof intro_classes
  obtain basisA :: 'a set where [simp]: cspan basisA = UNIV cindependent basisA finite basisA
    using finite_basis by blast
  obtain basisB :: 'b set where [simp]: cspan basisB = UNIV cindependent basisB finite basisB
    using finite_basis by blast
  define f where f a b = cconstruct basisA (λx. if x=a then b else 0) for a :: 'a and b :: 'b
  have f_a: f a b a = b if a : basisA for a b
    by (simp add: complex_vector.construct_basis f_def that)
  have f_not_a: f a b c = 0 if a : basisA and c : basisA and a  cfor a b c
    using that by (simp add: complex_vector.construct_basis f_def)
  define F where F a b = CBlinfun (f a b) for a b
  have clinear (f a b) for a b
    by (auto intro: complex_vector.linear_construct simp: f_def)
  then have bounded_clinear (f a b) for a b
    by auto
  then have F_apply: cblinfun_apply (F a b) = f a b for a b
    by (simp add: F_def bounded_clinear_CBlinfun_apply)
  define basis where basis = {F a b| a b. abasisA  bbasisB}
  have "a b. a  basisA; b  basisB  Fbasis. a'basisA. F *V a' = (if a' = a then b else 0)"
    by (smt (verit, del_insts) F_apply basis_def f_a f_not_a mem_Collect_eq)
  then have cspan basis = UNIV
    by (metis cspan basisA = UNIV cspan basisB = UNIV cblinfun_cspan_UNIV)

  moreover have finite basis
    unfolding basis_def by (auto intro: finite_image_set2) 
  ultimately show S :: ('a CL 'b) set. finite S  cspan S = UNIV
    by auto
qed

lemma norm_cblinfun_bound_dense:
  assumes 0  b
  assumes S: closure S = UNIV
  assumes bound: x. xS  norm (cblinfun_apply f x)  b * norm x
  shows norm f  b
proof -
  have 1: continuous_on UNIV (λa. norm (f *V a))
    by (simp add: continuous_on_eq_continuous_within)
  have 2: continuous_on UNIV (λa. b * norm a)
    using continuous_on_mult_left continuous_on_norm_id by blast
  have norm (cblinfun_apply f x)  b * norm x for x
    by (metis (mono_tags, lifting) UNIV_I S bound 1 2 on_closure_leI)
  then show norm f  b
    using 0  b norm_cblinfun_bound by blast
qed

lemma infsum_cblinfun_apply:
  assumes g summable_on S
  shows infsum (λx. A *V g x) S = A *V (infsum g S)
  using infsum_bounded_linear[unfolded o_def] assms cblinfun.real.bounded_linear_right by blast

lemma has_sum_cblinfun_apply:
  assumes (g has_sum x) S
  shows ((λx. A *V g x) has_sum (A *V x)) S
  using assms has_sum_bounded_linear[unfolded o_def] using cblinfun.real.bounded_linear_right by blast

lemma abs_summable_on_cblinfun_apply:
  assumes g abs_summable_on S
  shows (λx. A *V g x) abs_summable_on S
  using bounded_clinear.bounded_linear[OF cblinfun.bounded_clinear_right] assms
  by (rule abs_summable_on_bounded_linear[unfolded o_def])

lemma summable_on_cblinfun_apply:
  assumes g summable_on S
  shows (λx. A *V g x) summable_on S
  using bounded_clinear.bounded_linear[OF cblinfun.bounded_clinear_right] assms
  by (rule summable_on_bounded_linear[unfolded o_def])

lemma summable_on_cblinfun_apply_left:
  assumes A summable_on S
  shows (λx. A x *V g) summable_on S
  using bounded_clinear.bounded_linear[OF cblinfun.bounded_clinear_left] assms
  by (rule summable_on_bounded_linear[unfolded o_def])

lemma abs_summable_on_cblinfun_apply_left:
  assumes A abs_summable_on S
  shows (λx. A x *V g) abs_summable_on S
  using bounded_clinear.bounded_linear[OF cblinfun.bounded_clinear_left] assms
  by (rule abs_summable_on_bounded_linear[unfolded o_def])
lemma infsum_cblinfun_apply_left:
  assumes A summable_on S
  shows infsum (λx. A x *V g) S = (infsum A S) *V g
  apply (rule infsum_bounded_linear[unfolded o_def, of λA. cblinfun_apply A g])
  using assms 
  by (auto simp add: bounded_clinear.bounded_linear bounded_cbilinear_cblinfun_apply)
lemma has_sum_cblinfun_apply_left:
  assumes (A has_sum x) S
  shows ((λx. A x *V g) has_sum (x *V g)) S
  apply (rule has_sum_bounded_linear[unfolded o_def, of λA. cblinfun_apply A g])
  using assms by (auto simp add: bounded_clinear.bounded_linear cblinfun.bounded_clinear_left)

text ‹The next eight lemmas logically belong in theoryComplex_Bounded_Operators.Complex_Inner_Product
  but the proofs use facts from this theory.›
lemma has_sum_cinner_left:
  assumes (f has_sum x) I
  shows ((λi. cinner a (f i)) has_sum cinner a x) I
  by (metis assms cblinfun_cinner_right.rep_eq has_sum_cblinfun_apply)

lemma summable_on_cinner_left:
  assumes f summable_on I
  shows (λi. cinner a (f i)) summable_on I
  by (metis assms has_sum_cinner_left summable_on_def)

lemma infsum_cinner_left:
  assumes φ summable_on I
  shows cinner ψ (iI. φ i) = (iI. cinner ψ (φ i))
  by (metis assms has_sum_cinner_left has_sum_infsum infsumI)

lemma has_sum_cinner_right:
  assumes (f has_sum x) I
  shows ((λi. f i C a) has_sum (x C a)) I
  using assms has_sum_bounded_linear[unfolded o_def] bounded_antilinear.bounded_linear 
    bounded_antilinear_cinner_left by blast


lemma summable_on_cinner_right:
  assumes f summable_on I
  shows (λi. f i C a) summable_on I
  by (metis assms has_sum_cinner_right summable_on_def)

lemma infsum_cinner_right:
  assumes φ summable_on I
  shows (iI. φ i) C ψ = (iI. φ i C ψ)
  by (metis assms has_sum_cinner_right has_sum_infsum infsumI)

lemma Cauchy_cinner_product_summable:
  assumes asum: a summable_on UNIV
  assumes bsum: b summable_on UNIV
  assumes finite X finite Y
  assumes pos: x y. x  X  y  Y  cinner (a x) (b y)  0
  shows absum: (λ(x, y). cinner (a x) (b y)) summable_on UNIV
proof -
  have ((x,y)F. norm (cinner (a x) (b y)))  norm (cinner (infsum a (-X)) (infsum b (-Y))) + norm (infsum a (-X)) + norm (infsum b (-Y)) + 1 
    if finite F and F  (-X)×(-Y) for F
  proof -
    from asum finite X
    have a summable_on (-X)
      by (simp add: Compl_eq_Diff_UNIV summable_on_cofin_subset)
    then obtain MA where finite MA and MA  -X
      and MA: G  MA  G  -X  finite G  norm (sum a G - infsum a (-X))  1 for G
      apply (simp add: summable_iff_has_sum_infsum has_sum_metric dist_norm)
      by (meson less_eq_real_def zero_less_one)
    
    from bsum finite Y
    have b summable_on (-Y)
      by (simp add: Compl_eq_Diff_UNIV summable_on_cofin_subset)
    then obtain MB where finite MB and MB  -Y
      and MB: G  MB  G  -Y  finite G  norm (sum b G - infsum b (-Y))  1 for G
      apply (simp add: summable_iff_has_sum_infsum has_sum_metric dist_norm)
      by (meson less_eq_real_def zero_less_one)

    define F1 F2 where F1 = fst ` F  MA and F2 = snd ` F  MB
    define t1 t2 where t1 = sum a F1 - infsum a (-X) and t2 = sum b F2 - infsum b (-Y)
  
    have [simp]: finite F1 finite F2
      using F1_def F2_def finite MA finite MB that by auto
    have [simp]: F1  -X F2  -Y
      using F  (-X)×(-Y) MA  -X MB  -Y
      by (auto simp: F1_def F2_def)
    from MA[OF _ F1  -X finite F1] have norm t1  1 
      by (auto simp: t1_def F1_def)
    from MB[OF _ F2  -Y finite F2] have norm t2  1 
      by (auto simp: t2_def F2_def)
    have [simp]: F  F1 × F2
      by (force simp: F1_def F2_def image_def)
    have ((x,y)F. norm (cinner (a x) (b y)))  ((x,y)F1×F2. norm (cinner (a x) (b y)))
      by (intro sum_mono2) auto
    also have "... = (xF1 × F2. norm (a (fst x) C b (snd x)))"
      by (auto  simp: case_prod_beta)
    also have "... =  norm (xF1 × F2. a (fst x) C b (snd x))"
    proof -
      have "(xF1 × F2. ¦a (fst x) C b (snd x)¦) = ¦xF1 × F2. a (fst x) C b (snd x)¦"
        by (smt (verit, best) pos sum.cong sum_nonneg ComplD SigmaE F1  - X F2  - Y abs_pos prod.sel subset_iff)
      then show ?thesis
        by (smt (verit) abs_complex_def norm_ge_zero norm_of_real o_def of_real_sum sum.cong sum_norm_le)
    qed
    also from pos have  = norm ((x,y)F1×F2. cinner (a x) (b y))
      by (auto simp: case_prod_beta)
    also have  = norm (cinner (sum a F1) (sum b F2))
      by (simp add: sum.cartesian_product sum_cinner)
    also have  = norm (cinner (infsum a (-X) + t1) (infsum b (-Y) + t2))
      by (simp add: t1_def t2_def)
    also have   norm (cinner (infsum a (-X)) (infsum b (-Y))) + norm (infsum a (-X)) * norm t2 + norm t1 * norm (infsum b (-Y)) + norm t1 * norm t2
      apply (simp add: cinner_add_right cinner_add_left)
      by (smt (verit, del_insts) complex_inner_class.Cauchy_Schwarz_ineq2 norm_triangle_ineq)
    also from norm t1  1 norm t2  1
    have   norm (cinner (infsum a (-X)) (infsum b (-Y))) + norm (infsum a (-X)) + norm (infsum b (-Y)) + 1
      by (auto intro!: add_mono mult_left_le mult_left_le_one_le mult_le_one)
    finally show ?thesis
      by -
  qed

  then have (λ(x, y). cinner (a x) (b y)) abs_summable_on (-X)×(-Y)
    apply (rule_tac nonneg_bdd_above_summable_on)
    by (auto intro!: bdd_aboveI2 simp: case_prod_unfold)
  then have 1: (λ(x, y). cinner (a x) (b y)) summable_on (-X)×(-Y)
    using abs_summable_summable by blast

  from bsum
  have (λy. b y) summable_on (-Y)
    by (simp add: Compl_eq_Diff_UNIV assms(4) summable_on_cofin_subset)
  then have (λy. cinner (a x) (b y)) summable_on (-Y) for x
    using summable_on_cinner_left by blast
  with finite X have 2: (λ(x, y). cinner (a x) (b y)) summable_on X×(-Y)
    by (force intro: summable_on_product_finite_left)

  from asum
  have (λx. a x) summable_on (-X)
    by (simp add: Compl_eq_Diff_UNIV assms(3) summable_on_cofin_subset)
  then have (λx. cinner (a x) (b y)) summable_on (-X) for y
    using summable_on_cinner_right by blast
  with finite Y have 3: (λ(x, y). cinner (a x) (b y)) summable_on (-X)×Y
    by (force intro: summable_on_product_finite_right)

  have 4: (λ(x, y). cinner (a x) (b y)) summable_on X×Y
    by (simp add: finite X finite Y)

  have §: "UNIV = ((-X) × -Y)  (X × -Y)  ((-X) × Y)  (X × Y)"
    by auto
  show ?thesis
    using 1 2 3 4 by (force simp: § intro!: summable_on_Un_disjoint) 
qed


text ‹A variant of @{thm [source] Series.Cauchy_product_sums} with term(*) replaced by termcinner.
   Differently from @{thm [source] Series.Cauchy_product_sums}, we do not require absolute summability
   of terma and termb individually but only unconditional summability of terma, termb, and their product.
   While on, e.g., reals, unconditional summability is equivalent to absolute summability, in
   general unconditional summability is a weaker requirement.

  Logically belong in theoryComplex_Bounded_Operators.Complex_Inner_Product
  but the proof uses facts from this theory.›
lemma 
  fixes a b :: "nat  'a::complex_inner"
  assumes asum: a summable_on UNIV
  assumes bsum: b summable_on UNIV
  assumes absum: (λ(x, y). cinner (a x) (b y)) summable_on UNIV
    (* ― ‹See @{thm [source] Cauchy_cinner_product_summable} or @{thm [source] Cauchy_cinner_product_summable'} for a way to rewrite this premise.› *)
  shows Cauchy_cinner_product_infsum: (k. ik. cinner (a i) (b (k - i))) = cinner (k. a k) (k. b k)
    and Cauchy_cinner_product_infsum_exists: (λk. ik. cinner (a i) (b (k - i))) summable_on UNIV
proof -
  have img: (λ(k::nat, i). (i, k - i)) ` {(k, i). i  k} = UNIV
    apply (auto simp: image_def)
    by (metis add.commute add_diff_cancel_right' diff_le_self)
  have inj: inj_on (λ(k::nat, i). (i, k - i)) {(k, i). i  k}
    by (smt (verit, del_insts) Pair_inject case_prodE case_prod_conv eq_diff_iff inj_onI mem_Collect_eq)
  have sigma: (SIGMA k:UNIV. {i. i  k}) = {(k, i). i  k}
    by auto

  from absum
  have §: (λ(x, y). cinner (a y) (b (x - y))) summable_on {(k, i). i  k}
    by (rule Cauchy_cinner_product_summable'[THEN iffD1])
  then have (λk. i|ik. cinner (a i) (b (k-i))) summable_on UNIV
    by (metis (mono_tags, lifting) sigma summable_on_Sigma_banach summable_on_cong)
  then show (λk. ik. cinner (a i) (b (k - i))) summable_on UNIV
    by (metis (mono_tags, lifting) atMost_def finite_Collect_le_nat infsum_finite summable_on_cong)

  have cinner (k. a k) (k. b k) = (k. l. cinner (a k) (b l))
    by (smt (verit, best) asum bsum infsum_cinner_left infsum_cinner_right infsum_cong)
  also have  = ((k,l). cinner (a k) (b l))
    by (smt (verit) UNIV_Times_UNIV infsum_Sigma'_banach infsum_cong local.absum)
  also have  = ((k, l)(λ(k, i). (i, k - i)) ` {(k, i). i  k}. cinner (a k) (b l))
    by (simp only: img)
  also have  = infsum ((λ(k, l). a k C b l)  (λ(k, i). (i, k - i))) {(k, i). i  k}
    using inj by (rule infsum_reindex)
  also have  = ((k,i)|ik. a i C b (k-i))
    by (simp add: o_def case_prod_unfold)
  also have  = (k. i|ik. a i C b (k-i))
    by (metis (no_types) § infsum_Sigma'_banach sigma)
  also have  = (k. ik. a i C b (k-i))
    by (simp add: atMost_def)
  finally show (k. ik. a i C b (k - i)) = (k. a k) C (k. b k)
    by simp
qed


lemma CBlinfun_plus: 
  assumes [simp]: bounded_clinear f bounded_clinear g
  shows CBlinfun (f + g) = CBlinfun f + CBlinfun g
  by (auto intro!: cblinfun_eqI simp: plus_fun_def bounded_clinear_add CBlinfun_inverse cblinfun.add_left)

lemma CBlinfun_scaleC:
  assumes bounded_clinear f
  shows CBlinfun (λy. c *C f y) = c *C CBlinfun f
  by (simp add: assms eq_onp_same_args scaleC_cblinfun.abs_eq)


lemma cinner_sup_norm_cblinfun:
  fixes A :: 'a::{complex_normed_vector,not_singleton} CL 'b::complex_inner
  shows norm A = (SUP (ψ,φ). cmod (cinner ψ (A *V φ)) / (norm ψ * norm φ))
  apply transfer
  apply (rule cinner_sup_onorm)
  by (simp add: bounded_clinear.bounded_linear)


lemma norm_cblinfun_Sup: norm A = (SUP ψ. norm (A *V ψ) / norm ψ)
  by (simp add: norm_cblinfun.rep_eq onorm_def)

lemma cblinfun_eq_on:
  fixes A B :: "'a::cbanach CL'b::complex_normed_vector"
  assumes "x. x  G  A *V x = B *V x" and t  closure (cspan G)
  shows "A *V t = B *V t"
  using assms
  apply transfer
  using bounded_clinear_eq_on_closure by blast

lemma cblinfun_eq_gen_eqI:
  fixes A B :: "'a::cbanach CL'b::complex_normed_vector"
  assumes "x. x  G  A *V x = B *V x" and ccspan G = 
  shows "A = B"
  by (metis assms cblinfun_eqI cblinfun_eq_on ccspan.rep_eq iso_tuple_UNIV_I top_ccsubspace.rep_eq)

declare cnj_bounded_antilinear[bounded_antilinear]

lemma Cblinfun_comp_bounded_cbilinear: bounded_clinear (CBlinfun o p) if bounded_cbilinear p
  by (metis bounded_cbilinear.bounded_clinear_prod_right bounded_cbilinear.prod_right_def comp_id map_fun_def that)

lemma Cblinfun_comp_bounded_sesquilinear: bounded_antilinear (CBlinfun o p) if bounded_sesquilinear p
  by (metis (mono_tags, opaque_lifting) bounded_clinear_CBlinfun_apply bounded_sesquilinear.bounded_clinear_right comp_apply that transfer_bounded_sesquilinear_bounded_antilinearI)

subsection ‹Relationship to real bounded operators (typ_ L _)›

instantiation blinfun :: (real_normed_vector, complex_normed_vector) "complex_normed_vector"
begin
lift_definition scaleC_blinfun :: complex 
 ('a::real_normed_vector, 'b::complex_normed_vector) blinfun 
 ('a, 'b) blinfun
  is λ c::complex. λ f::'a'b. (λ x. c *C (f x) )
proof
  fix c::complex and f :: 'a'b and b1::'a and b2::'a
  assume bounded_linear f
  show c *C f (b1 + b2) = c *C f b1 + c *C f b2
    by (simp add: bounded_linear f linear_simps scaleC_add_right)

  fix c::complex and f :: 'a'b and b::'a and r::real
  assume bounded_linear f
  show c *C f (r *R b) = r *R (c *C f b)
    by (simp add: bounded_linear f linear_simps(5) scaleR_scaleC)

  fix c::complex and f :: 'a'b
  assume bounded_linear f

  have  K.  x. norm (f x)  norm x * K
    using bounded_linear f
    by (simp add: bounded_linear.bounded)
  then obtain K where  x. norm (f x)  norm x * K
    by blast
  have cmod c  0
    by simp
  hence  x. (cmod c) * norm (f x)  (cmod c) * norm x * K
    using   x. norm (f x)  norm x * K
    by (metis ordered_comm_semiring_class.comm_mult_left_mono vector_space_over_itself.scale_scale)
  moreover have norm (c *C f x) = (cmod c) * norm (f x)
    for x
    by simp
  ultimately show K. x. norm (c *C f x)  norm x * K
    by (metis ab_semigroup_mult_class.mult_ac(1) mult.commute)
qed

instance
proof
  have "r *R x = complex_of_real r *C x"
    for x :: "('a, 'b) blinfun" and r
    by transfer (simp add: scaleR_scaleC)
  thus "((*R) r::'a L 'b  _) = (*C) (complex_of_real r)" for r
    by auto
  show "a *C (x + y) = a *C x + a *C y"
    for a :: complex and x y :: "'a L 'b"
    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 L 'b"
    by transfer (simp add: scaleC_add_left)

  show "a *C b *C x = (a * b) *C x"
    for a b :: complex and x :: "'a L 'b"
    by transfer simp

  have 1 *C f x = f x
    for f :: 'a'b and x
    by auto
  thus "1 *C x = x"
    for x :: "'a L 'b"
    by (simp add: scaleC_blinfun.rep_eq blinfun_eqI)

  have onorm (λx. a *C f x) = cmod a * onorm f
    if bounded_linear f
    for f :: 'a  'b and a :: complex
  proof-
    have cmod a  0
      by simp
    have  K::real.  x. (¦ ereal ((norm (f x)) / (norm x)) ¦)  K
      using bounded_linear f le_onorm by fastforce
    then obtain K::real where  x. (¦ ereal ((norm (f x)) / (norm x)) ¦)  K
      by blast
    hence   x. (cmod a) *(¦ ereal ((norm (f x)) / (norm x)) ¦)  (cmod a) * K
      using cmod a  0
      by (metis abs_ereal.simps(1) abs_ereal_pos   abs_pos ereal_mult_left_mono  times_ereal.simps(1))
    hence   x.  (¦ ereal ((cmod a) * (norm (f x)) / (norm x)) ¦)  (cmod a) * K
      by simp
    hence bdd_above {ereal (cmod a * (norm (f x)) / (norm x)) | x. True}
      by simp
    moreover have {ereal (cmod a * (norm (f x)) / (norm x)) | x. True}  {}
      by auto
    ultimately have p1: (SUP x. ¦ereal (cmod a * (norm (f x)) / (norm x))¦)  cmod a * K
      using  x. ¦ ereal (cmod a * (norm (f x)) / (norm x)) ¦  cmod a * K
        Sup_least mem_Collect_eq
      by (simp add: SUP_le_iff)
    have  p2: i. i  UNIV  0  ereal (cmod a * norm (f i) / norm i)
      by simp
    hence ¦SUP x. ereal (cmod a * (norm (f x)) / (norm x))¦
               (SUP x. ¦ereal (cmod a * (norm (f x)) / (norm x))¦)
      using  bdd_above {ereal (cmod a * (norm (f x)) / (norm x)) | x. True}
        {ereal (cmod a * (norm (f x)) / (norm x)) | x. True}  {}
      by (metis (mono_tags, lifting) SUP_upper2 Sup.SUP_cong UNIV_I
          p2 abs_ereal_ge0 ereal_le_real)
    hence ¦SUP x. ereal (cmod a * (norm (f x)) / (norm x))¦  cmod a * K
      using  (SUP x. ¦ereal (cmod a * (norm (f x)) / (norm x))¦)  cmod a * K
      by simp
    hence ¦ ( SUP iUNIV::'a set. ereal ((λ x. (cmod a) * (norm (f x)) / norm x) i)) ¦  
      by auto
    hence w2: ( SUP iUNIV::'a set. ereal ((λ x. cmod a * (norm (f x)) / norm x) i))
             = ereal ( Sup ((λ x. cmod a * (norm (f x)) / norm x) ` (UNIV::'a set) ))
      by (simp add: ereal_SUP)
    have (UNIV::('a set))  {}
      by simp
    moreover have  i. i  (UNIV::('a set))  (λ x. (norm (f x)) / norm x :: ereal) i  0
      by simp
    moreover have cmod a  0
      by simp
    ultimately have (SUP i(UNIV::('a set)). ((cmod a)::ereal) * (λ x. (norm (f x)) / norm x :: ereal) i )
        = ((cmod a)::ereal) * ( SUP i(UNIV::('a set)). (λ x. (norm (f x)) / norm x :: ereal) i )
      by (simp add: Sup_ereal_mult_left')
    hence (SUP x. ((cmod a)::ereal) * ( (norm (f x)) / norm x :: ereal) )
        = ((cmod a)::ereal) * ( SUP x. ( (norm (f x)) / norm x :: ereal) )
      by simp
    hence z1: real_of_ereal ( (SUP x. ((cmod a)::ereal) * ( (norm (f x)) / norm x :: ereal) ) )
        = real_of_ereal ( ((cmod a)::ereal) * ( SUP x. ( (norm (f x)) / norm x :: ereal) ) )
      by simp
    have z2: real_of_ereal (SUP x. ((cmod a)::ereal) * ( (norm (f x)) / norm x :: ereal) )
                  = (SUP x. cmod a * (norm (f x) / norm x))
      using w2
      by auto
    have real_of_ereal ( ((cmod a)::ereal) * ( SUP x. ( (norm (f x)) / norm x :: ereal) ) )
                =  (cmod a) * real_of_ereal ( SUP x. ( (norm (f x)) / norm x :: ereal) )
      by simp
    moreover have real_of_ereal ( SUP x. ( (norm (f x)) / norm x :: ereal) )
                  = ( SUP x. ((norm (f x)) / norm x) )
    proof-
      have ¦ ( SUP iUNIV::'a set. ereal ((λ x. (norm (f x)) / norm x) i)) ¦  
      proof-
        have  K::real.  x. (¦ ereal ((norm (f x)) / (norm x)) ¦)  K
          using bounded_linear f le_onorm by fastforce
        then obtain K::real where  x. (¦ ereal ((norm (f x)) / (norm x)) ¦)  K
          by blast
        hence bdd_above {ereal ((norm (f x)) / (norm x)) | x. True}
          by simp
        moreover have {ereal ((norm (f x)) / (norm x)) | x. True}  {}
          by auto
        ultimately have (SUP x. ¦ereal ((norm (f x)) / (norm x))¦)  K
          using  x. ¦ ereal ((norm (f x)) / (norm x)) ¦  K
            Sup_least mem_Collect_eq
          by (simp add: SUP_le_iff)
        hence ¦SUP x. ereal ((norm (f x)) / (norm x))¦
               (SUP x. ¦ereal ((norm (f x)) / (norm x))¦)
          using  bdd_above {ereal ((norm (f x)) / (norm x)) | x. True}
            {ereal ((norm (f x)) / (norm x)) | x. True}  {}
          by (metis (mono_tags, lifting) SUP_upper2 Sup.SUP_cong UNIV_I i. i  UNIV  0  ereal (norm (f i) / norm i) abs_ereal_ge0 ereal_le_real)
        hence ¦SUP x. ereal ((norm (f x)) / (norm x))¦  K
          using  (SUP x. ¦ereal ((norm (f x)) / (norm x))¦)  K
          by simp
        thus ?thesis
          by auto
      qed
      hence ( SUP iUNIV::'a set. ereal ((λ x. (norm (f x)) / norm x) i))
             = ereal ( Sup ((λ x. (norm (f x)) / norm x) ` (UNIV::'a set) ))
        by (simp add: ereal_SUP)
      thus ?thesis
        by simp
    qed
    have z3: real_of_ereal ( ((cmod a)::ereal) * ( SUP x. ( (norm (f x)) / norm x :: ereal) ) )
                = cmod a * (SUP x. norm (f x) / norm x)
      by (simp add: real_of_ereal (SUP x. ereal (norm (f x) / norm x)) = (SUP x. norm (f x) / norm x))
    hence w1: (SUP x. cmod a * (norm (f x) / norm x)) =
          cmod a * (SUP x. norm (f x) / norm x)
      using z1 z2 by linarith
    have v1: onorm (λx. a *C f x) = (SUP x. norm (a *C f x) / norm x)
      by (simp add: onorm_def)
    have v2: (SUP x. norm (a *C f x) / norm x) = (SUP x. ((cmod a) * norm (f x)) / norm x)
      by simp
    have v3: (SUP x. ((cmod a) * norm (f x)) / norm x) =  (SUP x. (cmod a) * ((norm (f x)) / norm x))
      by simp
    have v4: (SUP x. (cmod a) * ((norm (f x)) / norm x)) = (cmod a) *  (SUP x. ((norm (f x)) / norm x))
      using w1
      by blast
    show onorm (λx. a *C f x) = cmod a * onorm f
      using v1 v2 v3 v4
      by (metis (mono_tags, lifting) onorm_def)
  qed
  thus norm (a *C x) = cmod a * norm x
    for a::complex and x::('a, 'b) blinfun
    by transfer blast
qed
end

(* We do not have clinear_blinfun_compose_right *)
lemma clinear_blinfun_compose_left: clinear (λx. blinfun_compose x y)
  by (auto intro!: clinearI simp: blinfun_eqI scaleC_blinfun.rep_eq bounded_bilinear.add_left
                                  bounded_bilinear_blinfun_compose)

instance blinfun :: (real_normed_vector, cbanach) cbanach..

lemma blinfun_compose_assoc: "(A oL B) oL C = A oL (B  oL C)"
  by (simp add: blinfun_eqI)

lift_definition blinfun_of_cblinfun::'a::complex_normed_vector CL 'b::complex_normed_vector
   'a L 'b is "id"
  by transfer (simp add: bounded_clinear.bounded_linear)

lift_definition blinfun_cblinfun_eq ::
  'a L 'b  'a::complex_normed_vector CL 'b::complex_normed_vector  bool is "(=)" .

lemma blinfun_cblinfun_eq_bi_unique[transfer_rule]: bi_unique blinfun_cblinfun_eq
  unfolding bi_unique_def by transfer auto

lemma blinfun_cblinfun_eq_right_total[transfer_rule]: right_total blinfun_cblinfun_eq
  unfolding right_total_def by transfer (simp add: bounded_clinear.bounded_linear)

named_theorems cblinfun_blinfun_transfer

lemma cblinfun_blinfun_transfer_0[cblinfun_blinfun_transfer]:
  "blinfun_cblinfun_eq (0::(_,_) blinfun) (0::(_,_) cblinfun)"
  by transfer simp

lemma cblinfun_blinfun_transfer_plus[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> blinfun_cblinfun_eq ===> blinfun_cblinfun_eq) (+) (+)"
  unfolding rel_fun_def by transfer auto

lemma cblinfun_blinfun_transfer_minus[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> blinfun_cblinfun_eq ===> blinfun_cblinfun_eq) (-) (-)"
  unfolding rel_fun_def by transfer auto

lemma cblinfun_blinfun_transfer_uminus[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> blinfun_cblinfun_eq) (uminus) (uminus)"
  unfolding rel_fun_def by transfer auto

definition "real_complex_eq r c  complex_of_real r = c"

lemma bi_unique_real_complex_eq[transfer_rule]: bi_unique real_complex_eq
  unfolding real_complex_eq_def bi_unique_def by auto

lemma left_total_real_complex_eq[transfer_rule]: left_total real_complex_eq
  unfolding real_complex_eq_def left_total_def by auto

lemma cblinfun_blinfun_transfer_scaleC[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(real_complex_eq ===> blinfun_cblinfun_eq ===> blinfun_cblinfun_eq) (scaleR) (scaleC)"
  unfolding rel_fun_def by transfer (simp add: real_complex_eq_def scaleR_scaleC)

lemma cblinfun_blinfun_transfer_CBlinfun[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(eq_onp bounded_clinear ===> blinfun_cblinfun_eq) Blinfun CBlinfun"
  unfolding rel_fun_def blinfun_cblinfun_eq.rep_eq eq_onp_def
  by (auto simp: CBlinfun_inverse Blinfun_inverse bounded_clinear.bounded_linear)

lemma cblinfun_blinfun_transfer_norm[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> (=)) norm norm"
  unfolding rel_fun_def by transfer auto

lemma cblinfun_blinfun_transfer_dist[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> blinfun_cblinfun_eq ===> (=)) dist dist"
  unfolding rel_fun_def dist_norm by transfer auto

lemma cblinfun_blinfun_transfer_sgn[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> blinfun_cblinfun_eq) sgn sgn"
  unfolding rel_fun_def sgn_blinfun_def sgn_cblinfun_def by transfer (auto simp: scaleR_scaleC)

lemma cblinfun_blinfun_transfer_Cauchy[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(((=) ===> blinfun_cblinfun_eq) ===> (=)) Cauchy Cauchy"
proof -
  note cblinfun_blinfun_transfer[transfer_rule]
  show ?thesis
    unfolding Cauchy_def
    by transfer_prover
qed

lemma cblinfun_blinfun_transfer_tendsto[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(((=) ===> blinfun_cblinfun_eq) ===> blinfun_cblinfun_eq ===> (=) ===> (=)) tendsto tendsto"
proof -
  note cblinfun_blinfun_transfer[transfer_rule]
  show ?thesis
    unfolding tendsto_iff
    by transfer_prover
qed

lemma cblinfun_blinfun_transfer_compose[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> blinfun_cblinfun_eq ===> blinfun_cblinfun_eq) (oL) (oCL)"
  unfolding rel_fun_def by transfer auto

lemma cblinfun_blinfun_transfer_apply[cblinfun_blinfun_transfer]:
  includes lifting_syntax
  shows "(blinfun_cblinfun_eq ===> (=) ===> (=)) blinfun_apply cblinfun_apply"
  unfolding rel_fun_def by transfer auto

lemma blinfun_of_cblinfun_inj:
  blinfun_of_cblinfun f = blinfun_of_cblinfun g  f = g
  by (metis cblinfun_apply_inject blinfun_of_cblinfun.rep_eq)

lemma blinfun_of_cblinfun_inv:
  assumes "c. x. f *v (c *C x) = c *C (f *v x)"
  shows "g. blinfun_of_cblinfun g = f"
  using assms
proof transfer
  show "gCollect bounded_clinear. id g = f"
    if "bounded_linear f"
      and "c x. f (c *C x) = c *C f x"
    for f :: "'a  'b"
    using that bounded_linear_bounded_clinear by auto
qed

lemma blinfun_of_cblinfun_zero:
  blinfun_of_cblinfun 0 = 0
  by transfer simp

lemma blinfun_of_cblinfun_uminus:
  blinfun_of_cblinfun (- f) = - (blinfun_of_cblinfun f)
  by transfer auto

lemma blinfun_of_cblinfun_minus:
  blinfun_of_cblinfun (f - g) = blinfun_of_cblinfun f - blinfun_of_cblinfun g
  by transfer auto

lemma blinfun_of_cblinfun_scaleC:
  blinfun_of_cblinfun (c *C f) = c *C (blinfun_of_cblinfun f)
  by transfer auto

lemma blinfun_of_cblinfun_scaleR:
  blinfun_of_cblinfun (c *R f) = c *R (blinfun_of_cblinfun f)
  by transfer auto

lemma blinfun_of_cblinfun_norm:
  fixes f::'a::complex_normed_vector CL 'b::complex_normed_vector
  shows norm f = norm (blinfun_of_cblinfun f)
  by transfer auto

lemma blinfun_of_cblinfun_cblinfun_compose:
  fixes f::'b::complex_normed_vector CL 'c::complex_normed_vector
    and g::'a::complex_normed_vector CL 'b
  shows blinfun_of_cblinfun (f  oCL g) = (blinfun_of_cblinfun f) oL (blinfun_of_cblinfun g)
  by transfer auto

subsection ‹Composition›

lemma cblinfun_compose_assoc:
  shows "(A oCL B) oCL C = A oCL (B oCL C)"
  by (metis (no_types, lifting) cblinfun_apply_inject fun.map_comp cblinfun_compose.rep_eq)

lemma cblinfun_compose_zero_right[simp]: "U oCL 0 = 0"
  using bounded_cbilinear.zero_right bounded_cbilinear_cblinfun_compose by blast

lemma cblinfun_compose_zero_left[simp]: "0 oCL U = 0"
  using bounded_cbilinear.zero_left bounded_cbilinear_cblinfun_compose by blast

lemma cblinfun_compose_scaleC_left[simp]:
  fixes A::"'b::complex_normed_vector CL 'c::complex_normed_vector"
    and B::"'a::complex_normed_vector CL 'b"
  shows (a *C A) oCL B = a *C (A oCL B)
  by (simp add: bounded_cbilinear.scaleC_left bounded_cbilinear_cblinfun_compose)

lemma cblinfun_compose_scaleR_left[simp]:
  fixes A::"'b::complex_normed_vector CL 'c::complex_normed_vector"
    and B::"'a::complex_normed_vector CL 'b"
  shows (a *R A) oCL B = a *R (A oCL B)
  by (simp add: scaleR_scaleC)

lemma cblinfun_compose_scaleC_right[simp]:
  fixes A::"'b::complex_normed_vector CL 'c::complex_normed_vector"
    and B::"'a::complex_normed_vector CL 'b"
  shows A oCL (a *C B) = a *C (A oCL B)
  by transfer (auto intro!: ext bounded_clinear.clinear complex_vector.linear_scale)

lemma cblinfun_compose_scaleR_right[simp]:
  fixes A::"'b::complex_normed_vector CL 'c::complex_normed_vector"
    and B::"'a::complex_normed_vector CL 'b"
  shows A oCL (a *R B) = a *R (A oCL B)
  by (simp add: scaleR_scaleC)

lemma cblinfun_compose_id_right[simp]:
  shows "U oCL id_cblinfun = U"
  by transfer auto

lemma cblinfun_compose_id_left[simp]:
  shows "id_cblinfun oCL U  = U"
  by transfer auto

lemma cblinfun_compose_add_left: (a + b) oCL c = (a oCL c) + (b oCL c)
  by (simp add: bounded_cbilinear.add_left bounded_cbilinear_cblinfun_compose)

lemma cblinfun_compose_add_right: a oCL (b + c) = (a oCL b) + (a oCL c)
  by (simp add: bounded_cbilinear.add_right bounded_cbilinear_cblinfun_compose)

lemma cbilinear_cblinfun_compose[simp]: "cbilinear cblinfun_compose"
  by (auto intro!: clinearI simp add: cbilinear_def bounded_cbilinear.add_left bounded_cbilinear.add_right bounded_cbilinear_cblinfun_compose)

lemma cblinfun_compose_sum_left: (iS. g i) oCL x = (iS. g i oCL x)
  by (induction S rule:infinite_finite_induct) (auto simp: cblinfun_compose_add_left)

lemma cblinfun_compose_sum_right: x oCL (iS. g i) = (iS. x oCL g i)
  by (induction S rule:infinite_finite_induct) (auto simp: cblinfun_compose_add_right)

lemma cblinfun_compose_minus_right: a oCL (b - c) = (a oCL b) - (a oCL c)
  by (simp add: bounded_cbilinear.diff_right bounded_cbilinear_cblinfun_compose)
lemma cblinfun_compose_minus_left: (a - b) oCL c = (a oCL c) - (b oCL c)
  by (simp add: bounded_cbilinear.diff_left bounded_cbilinear_cblinfun_compose)


lemma simp_a_oCL_b: a oCL b = c  a oCL (b oCL d) = c oCL d
  ― ‹A convenience lemma to transform simplification rules of the form terma oCL b = c.
     E.g., simp_a_oCL_b[OF isometryD, simp]› declares a simp-rule for simplifying termadj x oCL (x oCL y) = id_cblinfun oCL y.›
  by (auto simp: cblinfun_compose_assoc)

lemma simp_a_oCL_b': a oCL b = c  a *V (b *V d) = c *V d
  ― ‹A convenience lemma to transform simplification rules of the form terma oCL b = c.
     E.g., simp_a_oCL_b'[OF isometryD, simp]› declares a simp-rule for simplifying termadj x *V x *V y = id_cblinfun *V y.›
  by auto

lemma cblinfun_compose_uminus_left: (- a) oCL b = - (a oCL b)
  by (simp add: bounded_cbilinear.minus_left bounded_cbilinear_cblinfun_compose)

lemma cblinfun_compose_uminus_right: a oCL (- b) = - (a oCL b)
  by (simp add: bounded_cbilinear.minus_right bounded_cbilinear_cblinfun_compose)

lemma bounded_clinear_cblinfun_compose_left: bounded_clinear (λx. x oCL y)
  by (simp add: bounded_cbilinear.bounded_clinear_left bounded_cbilinear_cblinfun_compose)
lemma bounded_clinear_cblinfun_compose_right: bounded_clinear (λy. x oCL y)
  by (simp add: bounded_cbilinear.bounded_clinear_right bounded_cbilinear_cblinfun_compose)
lemma clinear_cblinfun_compose_left: clinear (λx. x oCL y)
  by (simp add: bounded_cbilinear.bounded_clinear_left bounded_cbilinear_cblinfun_compose bounded_clinear.clinear)
lemma clinear_cblinfun_compose_right: clinear (λy. x oCL y)
  by (simp add: bounded_clinear.clinear bounded_clinear_cblinfun_compose_right)

lemma additive_cblinfun_compose_left[simp]: Modules.additive (λx. x oCL a)
  by (simp add: Modules.additive_def cblinfun_compose_add_left)
lemma additive_cblinfun_compose_right[simp]: Modules.additive (λx. a oCL x)
  by (simp add: Modules.additive_def cblinfun_compose_add_right)
lemma isCont_cblinfun_compose_left: isCont (λx. x oCL a) y
  apply (rule clinear_continuous_at)
  by (rule bounded_clinear_cblinfun_compose_left)
lemma isCont_cblinfun_compose_right: isCont (λx. a oCL x) y
  apply (rule clinear_continuous_at)
  by (rule bounded_clinear_cblinfun_compose_right)

lemma cspan_compose_closed:
  assumes a b. a  A  b  A  a oCL b  A
  assumes a  cspan A and b  cspan A
  shows a oCL b  cspan A
proof -
  from a  cspan A
  obtain F f where finite F and F  A and a_def: a = (xF. f x *C x)
    by (smt (verit, del_insts) complex_vector.span_explicit mem_Collect_eq)
  from b  cspan A
  obtain G g where finite G and G  A and b_def: b = (xG. g x *C x)
    by (smt (verit, del_insts) complex_vector.span_explicit mem_Collect_eq)
  have a oCL b = ((x,y)F×G. (f x *C x) oCL (g y *C y))
    apply (simp add: a_def b_def cblinfun_compose_sum_left)
    by (auto intro!: sum.cong simp add: cblinfun_compose_sum_right scaleC_sum_right sum.cartesian_product case_prod_beta)
  also have  = ((x,y)F×G. (f x * g y) *C (x oCL y))
    by (metis (no_types, opaque_lifting) cblinfun_compose_scaleC_left cblinfun_compose_scaleC_right scaleC_scaleC)
  also have   cspan A
    using assms G  A F  A
    apply (auto intro!: complex_vector.span_sum complex_vector.span_scale 
        simp: complex_vector.span_clauses)
    using complex_vector.span_clauses(1) by blast
  finally show ?thesis
    by -
qed

subsection ‹Adjoint›

lift_definition
  adj :: "'a::chilbert_space CL 'b::complex_inner  'b CL 'a" ("_*" [99] 100)
  is cadjoint by (fact cadjoint_bounded_clinear)

definition selfadjoint :: ('a::chilbert_space CL 'a)  bool where
  selfadjoint a  a* = a

lemma id_cblinfun_adjoint[simp]: "id_cblinfun* = id_cblinfun"
  by (metis adj.rep_eq apply_id_cblinfun cadjoint_id cblinfun_apply_inject)

lemma double_adj[simp]: "(A*)* = A"
  apply transfer using double_cadjoint by blast

lemma adj_cblinfun_compose[simp]:
  fixes B::'a::chilbert_space CL 'b::chilbert_space
    and A::'b CL 'c::complex_inner
  shows "(A oCL B)* =  (B*) oCL (A*)"
proof transfer
  fix  A :: 'b  'c and B :: 'a  'b
  assume bounded_clinear A and bounded_clinear B
  hence bounded_clinear (A  B)
    by (simp add: comp_bounded_clinear)
  have ((A  B) u C v) = (u C (B  A) v)
    for u v
    by (metis (no_types, lifting) cadjoint_univ_prop bounded_clinear A bounded_clinear B cinner_commute' comp_def)
  thus (A  B) = B  A
    using bounded_clinear (A  B)
    by (metis cadjoint_eqI cinner_commute')
qed

lemma scaleC_adj[simp]: "(a *C A)* = (cnj a) *C (A*)"
  by transfer (simp add: bounded_clinear.bounded_linear bounded_clinear_def complex_vector.linear_scale scaleC_cadjoint)

lemma scaleR_adj[simp]: "(a *R A)* = a *R (A*)"
  by (simp add: scaleR_scaleC)

lemma adj_plus: (A + B)* = (A*) + (B*)
proof transfer
  fix A B::'b  'a
  assume a1: bounded_clinear A and a2: bounded_clinear B
  define F where F = (λx. (A) x + (B) x)
  define G where G = (λx. A x + B x)
  have bounded_clinear G
    unfolding G_def
    by (simp add: a1 a2 bounded_clinear_add)
  moreover have (F u C v) = (u C G v) for u v
    unfolding F_def G_def
    using cadjoint_univ_prop a1 a2 cinner_add_left
    by (simp add: cadjoint_univ_prop cinner_add_left cinner_add_right)
  ultimately have F = G
    using cadjoint_eqI by blast
  thus (λx. A x + B x) = (λx. (A) x + (B) x)
    unfolding F_def G_def
    by auto
qed

lemma cinner_adj_left:
  fixes G :: "'b::chilbert_space CL 'a::complex_inner"
  shows (G* *V x) C y = x C (G *V y)
  apply transfer using cadjoint_univ_prop by blast

lemma cinner_adj_right:
  fixes G :: "'b::chilbert_space CL 'a::complex_inner"
  shows x C (G* *V y) = (G *V x) C y
  apply transfer using cadjoint_univ_prop' by blast

lemma adj_0[simp]: 0* = 0
  by (metis add_cancel_right_left adj_plus)

lemma selfadjoint_0[simp]: selfadjoint 0
  by (simp add: selfadjoint_def)

lemma norm_adj[simp]: norm (A*) = norm A
  for A :: 'b::chilbert_space CL 'c::complex_inner
proof (cases (x y :: 'b. x  y)  (x y :: 'c. x  y))
  case True
  then have c1: class.not_singleton TYPE('b)
    by intro_classes simp
  from True have c2: class.not_singleton TYPE('c)
    by intro_classes simp
  have normA: norm A = (SUP (ψ, φ). cmod (ψ C (A *V φ)) / (norm ψ * norm φ))
    apply (rule cinner_sup_norm_cblinfun[internalize_sort 'a::{complex_normed_vector,not_singleton}])
     apply (rule complex_normed_vector_axioms)
    by (rule c1)
  have normAadj: norm (A*) = (SUP (ψ, φ). cmod (ψ C (A* *V φ)) / (norm ψ * norm φ))
    apply (rule cinner_sup_norm_cblinfun[internalize_sort 'a::{complex_normed_vector,not_singleton}])
     apply (rule complex_normed_vector_axioms)
    by (rule c2)

  have norm (A*) = (SUP (ψ, φ). cmod (φ C (A *V ψ)) / (norm ψ * norm φ))
    unfolding normAadj
    apply (subst cinner_adj_right)
    apply (subst cinner_commute)
    apply (subst complex_mod_cnj)
    by rule
  also have  = Sup ((λ(ψ, φ). cmod (φ C (A *V ψ)) / (norm ψ * norm φ)) ` prod.swap ` UNIV)
    by auto
  also have  = (SUP (φ, ψ). cmod (φ C (A *V ψ)) / (norm ψ * norm φ))
    apply (subst image_image)
    by auto
  also have  = norm A
    unfolding normA
    by (simp add: mult.commute)
  finally show ?thesis
    by -
next
  case False
  then consider (b) x::'b. x = 0 | (c) x::'c. x = 0
    by auto
  then have A = 0
    apply (cases; transfer)
     apply (metis (full_types) bounded_clinear_def complex_vector.linear_0)
    by auto
  then show norm (A*) = norm A
    by simp
qed


lemma antilinear_adj[simp]: antilinear adj
  by (simp add: adj_plus antilinearI)

lemma bounded_antilinear_adj[bounded_antilinear, simp]: bounded_antilinear adj
  by (auto intro!: antilinearI exI[of _ 1] simp: bounded_antilinear_def bounded_antilinear_axioms_def adj_plus)

lemma adjoint_eqI:
  fixes G:: 'b::chilbert_space CL 'a::complex_inner
    and F:: 'a CL 'b
  assumes x y. ((cblinfun_apply F) x C y) = (x C (cblinfun_apply G) y)
  shows F = G*
  using assms apply transfer using cadjoint_eqI by auto

lemma adj_uminus: (-A)* = - (A*)
  by (metis scaleR_adj scaleR_minus1_left scaleR_minus1_left)

lemma cinner_real_hermiteanI:
  ― ‹Prop. II.2.12 in citeconway2013course
  assumes ψ. ψ C (A *V ψ)  
  shows A* = A
proof -
  { fix g h :: 'a
    {
      fix α :: complex
      have cinner h (A h) + cnj α *C cinner g (A h) + α *C cinner h (A g) + (abs α)2 * cinner g (A g)
        = cinner (h + α *C g) (A *V (h + α *C g)) (is ?sum4 = _)
        apply (auto simp: cinner_add_right cinner_add_left cblinfun.add_right cblinfun.scaleC_right ring_class.ring_distribs)
        by (metis cnj_x_x mult.commute)
      also have   
        using assms by auto
      finally have ?sum4 = cnj ?sum4
        using Reals_cnj_iff by fastforce
      then have cnj α *C cinner g (A h) + α *C cinner h (A g)
            = α *C cinner (A h) g + cnj α *C cinner (A g) h
        using Reals_cnj_iff abs_complex_real assms by force
      also have  = α *C cinner h (A* *V g) + cnj α *C cinner g (A* *V h)
        by (simp add: cinner_adj_right)
      finally have cnj α *C cinner g (A h) + α *C cinner h (A g) = α *C cinner h (A* *V g) + cnj α *C cinner g (A* *V h)
        by -
    }
    from this[where α2=1] this[where α2=𝗂]
    have 1: cinner g (A h) + cinner h (A g) = cinner h (A* *V g) + cinner g (A* *V h)
      and i: - 𝗂 * cinner g (A h) + 𝗂 *C cinner h (A g) =  𝗂 *C cinner h (A* *V g) - 𝗂 *C cinner g (A* *V h)
      by auto
    from arg_cong2[OF 1 arg_cong[OF i, where f=(*) (-𝗂)], where f=plus]
    have cinner h (A g) = cinner h (A* *V g)
      by (auto simp: ring_class.ring_distribs)
  }
  then show "A* = A"
    apply (rule_tac sym)
    by (simp add: adjoint_eqI cinner_adj_right)
qed


lemma norm_AAadj[simp]: norm (A oCL A*) = (norm A)2 for A :: 'a::chilbert_space CL 'b::{complex_inner}
proof (cases class.not_singleton TYPE('b))
  case True
  then have [simp]: class.not_singleton TYPE('b)
    by -
  have 1: (norm A)2 * ε  norm (A oCL A*) if ε < 1 and ε  0 for ε
  proof -
    obtain ψ where ψ: norm ((A*) *V ψ)  norm (A*) * sqrt ε and [simp]: norm ψ = 1
      apply atomize_elim
      apply (rule cblinfun_norm_approx_witness_mult[internalize_sort' 'a])
      using ε < 1 by (auto intro: complex_normed_vector_class.complex_normed_vector_axioms)
    have complex_of_real ((norm A)2 * ε) = (norm (A*) * sqrt ε)2
      by (simp add: ordered_field_class.sign_simps(23) that(2))
    also have   (norm ((A* *V ψ)))2
      by (meson ψ complex_of_real_mono mult_nonneg_nonneg norm_ge_zero power_mono real_sqrt_ge_zero ε  0)
    also have   cinner (A* *V ψ) (A* *V ψ)
      by (auto simp flip: power2_norm_eq_cinner)
    also have §:  = cinner ψ ((A oCL A*) *V ψ)
      by (auto simp: cinner_adj_left)
    also have   norm (A oCL A*)
      using norm ψ = 1
      by (smt (verit) Re_complex_of_real § cdot_square_norm cinner_ge_zero cmod_Re complex_inner_class.Cauchy_Schwarz_ineq2 complex_of_real_mono mult_cancel_left1 mult_cancel_right1 norm_cblinfun)
    finally show ?thesis
      by (auto simp: less_eq_complex_def)
  qed
  then have 1: (norm A)2  norm (A oCL A*)
    by (metis field_le_mult_one_interval less_eq_real_def ordered_field_class.sign_simps(5))

  have 2: norm (A oCL A*)  (norm A)2
  proof (rule norm_cblinfun_bound)
    show 0  (norm A)2 by simp
    fix ψ
    have norm ((A oCL A*) *V ψ) = norm (A *V A* *V ψ)
      by auto
    also have   norm A * norm (A* *V ψ)
      by (simp add: norm_cblinfun)
    also have   norm A * norm (A*) * norm ψ
      by (metis mult.assoc norm_cblinfun norm_imp_pos_and_ge ordered_comm_semiring_class.comm_mult_left_mono)
    also have  = (norm A)2 * norm ψ
      by (simp add: power2_eq_square)
    finally show norm ((A oCL A*) *V ψ)  (norm A)2 * norm ψ
      by -
  qed

  from 1 2 show ?thesis by simp
next
  case False
  then have [simp]: class.CARD_1 TYPE('b)
    by (rule not_singleton_vs_CARD_1)
  have A = 0
    apply (rule cblinfun_to_CARD_1_0[internalize_sort' 'b])
    by (auto intro: complex_normed_vector_class.complex_normed_vector_axioms)
  then show ?thesis
    by auto
qed

lemma sum_adj: (sum a F)* = sum (λi. (a i)*) F
  by (induction rule:infinite_finite_induct) (auto simp add: adj_plus)

lemma has_sum_adj:
  assumes (f has_sum x) I
  shows ((λx. adj (f x)) has_sum adj x) I

  apply (rule has_sum_comm_additive[where f=adj, unfolded o_def])
  apply (simp add: antilinear.axioms(1))
  apply (metis (no_types, lifting) LIM_eq adj_plus adj_uminus norm_adj uminus_add_conv_diff)
  by (simp add: assms)

lemma adj_minus: (A - B)* = (A*) - (B*)
  by (metis add_implies_diff adj_plus diff_add_cancel)

lemma cinner_hermitian_real: x C (A *V x)   if selfadjoint A
  by (metis Reals_cnj_iff cinner_adj_right cinner_commute' that selfadjoint_def)

lemma adj_inject: adj a = adj b  a = b
  by (metis (no_types, opaque_lifting) adj_minus eq_iff_diff_eq_0 norm_adj norm_eq_zero)

lemma norm_AadjA[simp]: norm (A* oCL A) = (norm A)2 for A :: 'a::chilbert_space CL 'b::chilbert_space
  by (metis double_adj norm_AAadj norm_adj)

lemma cspan_adj_closed:
  assumes a. a  A  a*  A
  assumes a  cspan A
  shows a*  cspan A
proof -
  from a  cspan A
  obtain F f where finite F and F  A and a = (xF. f x *C x)
    by (smt (verit, del_insts) complex_vector.span_explicit mem_Collect_eq)
  then have a* = (xF. cnj (f x) *C x*)
    by (auto simp: sum_adj)
  also have   cspan A
    using assms F  A
    by (auto intro!: complex_vector.span_sum complex_vector.span_scale simp: complex_vector.span_clauses)
  finally show ?thesis
    by -
qed

subsection ‹Powers of operators›

lift_definition cblinfun_power :: 'a::complex_normed_vector CL 'a  nat  'a CL 'a is
  λ(a::'a'a) n. a ^^ n
  apply (rename_tac f n, induct_tac n, auto simp: Nat.funpow_code_def)
  by (simp add: bounded_clinear_compose)

lemma cblinfun_power_0[simp]: cblinfun_power A 0 = id_cblinfun 
  by transfer auto

lemma cblinfun_power_Suc': cblinfun_power A (Suc n) = A oCL cblinfun_power A n 
  by transfer auto

lemma cblinfun_power_Suc: cblinfun_power A (Suc n) = cblinfun_power A n oCL A
  apply (induction n)
  by (auto simp: cblinfun_power_Suc' simp flip:  cblinfun_compose_assoc)

lemma cblinfun_power_compose[simp]: cblinfun_power A n oCL cblinfun_power A m = cblinfun_power A (n+m)
  apply (induction n)
  by (auto simp: cblinfun_power_Suc' cblinfun_compose_assoc)

lemma cblinfun_power_scaleC: cblinfun_power (c *C a) n = c^n *C cblinfun_power a n
  apply (induction n)
  by (auto simp: cblinfun_power_Suc)

lemma cblinfun_power_scaleR: cblinfun_power (c *R a) n = c^n *R cblinfun_power a n
  apply (induction n)
  by (auto simp: cblinfun_power_Suc)

lemma cblinfun_power_uminus: cblinfun_power (-a) n = (-1)^n *R cblinfun_power a n
  apply (subst asm_rl[of -a = (-1) *R a])
   by simp (rule cblinfun_power_scaleR)

lemma cblinfun_power_adj: (cblinfun_power S n)* = cblinfun_power (S*) n
  apply (induction n)
   apply simp
  apply (subst cblinfun_power_Suc)
  apply (subst cblinfun_power_Suc')
  by auto

subsection ‹Unitaries / isometries›


definition isometry::'a::chilbert_space CL 'b::complex_inner  bool where
  isometry U  U* oCL U = id_cblinfun

definition unitary::'a::chilbert_space CL 'b::complex_inner  bool where
  unitary U  (U* oCL U  = id_cblinfun)  (U oCL U* = id_cblinfun)

lemma unitaryI: unitary a if a* oCL a = id_cblinfun and a oCL a* = id_cblinfun
  unfolding unitary_def using that by simp

lemma unitary_twosided_isometry: "unitary U  isometry U  isometry (U*)"
  unfolding unitary_def isometry_def by simp

lemma isometryD[simp]: "isometry U  U* oCL U = id_cblinfun"
  unfolding isometry_def by simp

(* Not [simp] because isometryD[simp] + unitary_isometry[simp] already have the same effect *)
lemma unitaryD1: "unitary U  U* oCL U = id_cblinfun"
  unfolding unitary_def by simp

lemma unitaryD2[simp]: "unitary U  U oCL U* = id_cblinfun"
  unfolding unitary_def by simp

lemma unitary_isometry[simp]: "unitary U  isometry U"
  unfolding unitary_def isometry_def by simp

lemma unitary_adj[simp]: "unitary (U*) = unitary U"
  unfolding unitary_def by auto

lemma isometry_cblinfun_compose[simp]:
  assumes "isometry A" and "isometry B"
  shows "isometry (A oCL B)"
proof-
  have "B* oCL A* oCL (A oCL B) = id_cblinfun" if "A* oCL A = id_cblinfun" and "B* oCL B = id_cblinfun"
    using that
    by (smt (verit, del_insts) adjoint_eqI cblinfun_apply_cblinfun_compose cblinfun_id_cblinfun_apply)
  thus ?thesis
    using assms unfolding isometry_def by simp
qed

lemma unitary_cblinfun_compose[simp]: "unitary (A oCL B)"
  if "unitary A" and "unitary B"
  using that
  by (smt (z3) adj_cblinfun_compose cblinfun_compose_assoc cblinfun_compose_id_right double_adj isometryD isometry_cblinfun_compose unitary_def unitary_isometry)

lemma unitary_surj:
  assumes "unitary U"
  shows "surj (cblinfun_apply U)"
  apply (rule surjI[where f=cblinfun_apply (U*)])
  using assms unfolding unitary_def apply transfer
  using comp_eq_dest_lhs by force

lemma unitary_id[simp]: "unitary id_cblinfun"
  by (simp add: unitary_def)

lemma orthogonal_on_basis_is_isometry:
  assumes spanB: ccspan B = 
  assumes orthoU: b c. bB  cB  cinner (U *V b) (U *V c) = cinner b c
  shows isometry U
proof -
  have [simp]: b  closure (cspan B) for b
    using spanB by transfer simp
  have *: cinner (U* *V U *V ψ) φ = cinner ψ φ if ψB and φB for ψ φ
    by (simp add: cinner_adj_left orthoU that(1) that(2))
  have *: cinner (U* *V U *V ψ) φ = cinner ψ φ if ψB for ψ φ
    apply (rule bounded_clinear_eq_on_closure[where t=φ and G=B])
    using bounded_clinear_cinner_right *[OF that]
    by auto
  have U* *V U *V φ = φ if φB for φ
    apply (rule cinner_extensionality)
    apply (subst cinner_eq_flip)
    by (simp add: * that)
  then have U* oCL U = id_cblinfun
    by (metis cblinfun_apply_cblinfun_compose cblinfun_eq_gen_eqI cblinfun_id_cblinfun_apply spanB)
  then show isometry U
    using isometry_def by blast
qed

lemma isometry_preserves_norm: isometry U  norm (U *V ψ) = norm ψ
  by (metis (no_types, lifting) cblinfun_apply_cblinfun_compose cblinfun_id_cblinfun_apply cinner_adj_right cnorm_eq isometryD)

lemma norm_isometry_compose: 
  assumes isometry U
  shows norm (U oCL A) = norm A
proof -
  have *: norm (U *V A *V ψ) = norm (A *V ψ) for ψ
    by (smt (verit, ccfv_threshold) assms cblinfun_apply_cblinfun_compose cinner_adj_right cnorm_eq id_cblinfun_apply isometryD)

  have norm (U oCL A) = (SUP ψ. norm (U *V A *V ψ) / norm ψ)
    unfolding norm_cblinfun_Sup by auto
  also have  = (SUP ψ. norm (A *V ψ) / norm ψ)
    using * by auto
  also have  = norm A
    unfolding norm_cblinfun_Sup by auto
  finally show ?thesis
    by simp
qed

lemma norm_isometry:
  fixes U :: 'a::{chilbert_space,not_singleton} CL 'b::complex_inner
  assumes isometry U
  shows norm U = 1
  apply (subst asm_rl[of U = U oCL id_cblinfun], simp)
  apply (subst norm_isometry_compose, simp add: assms)
  by simp

lemma norm_preserving_isometry: isometry U if ψ. norm (U *V ψ) = norm ψ
  by (smt (verit, ccfv_SIG) cblinfun_cinner_eqI cblinfun_id_cblinfun_apply cinner_adj_right cnorm_eq isometry_def simp_a_oCL_b' that)

lemma norm_isometry_compose': norm (A oCL U) = norm A if isometry (U*)
  by (smt (verit) cblinfun_compose_assoc cblinfun_compose_id_right double_adj isometryD mult_cancel_left2 norm_AadjA norm_cblinfun_compose norm_isometry_compose norm_zero power2_eq_square right_diff_distrib that zero_less_norm_iff)

lemma unitary_nonzero[simp]: ¬ unitary (0 :: 'a::{chilbert_space, not_singleton} CL _)
  by (simp add: unitary_def)

lemma isometry_inj:
  assumes isometry U
  shows inj_on U X
  apply (rule inj_on_inverseI[where g=U*])
  using assms by (simp flip: cblinfun_apply_cblinfun_compose)

lemma unitary_inj:
  assumes unitary U
  shows inj_on U X
  apply (rule isometry_inj)
  using assms by simp

lemma unitary_adj_inv: unitary U  cblinfun_apply (U*) = inv (cblinfun_apply U)
  apply (rule inj_imp_inv_eq[symmetric])
   apply (simp add: unitary_inj)
  unfolding unitary_def
  by (simp flip: cblinfun_apply_cblinfun_compose)

lemma isometry_cinner_both_sides:
  assumes isometry U
  shows cinner (U x) (U y) = cinner x y
  using assms by (simp add: flip: cinner_adj_right cblinfun_apply_cblinfun_compose)

lemma isometry_image_is_ortho_set:
  assumes is_ortho_set A
  assumes isometry U
  shows is_ortho_set (U ` A)
  using assms apply (auto simp add: is_ortho_set_def isometry_cinner_both_sides)
  by (metis cinner_eq_zero_iff isometry_cinner_both_sides)

subsection ‹Product spaces›

lift_definition cblinfun_left :: 'a::complex_normed_vector CL ('a×'b::complex_normed_vector) is (λx. (x,0))
  by (auto intro!: bounded_clinearI[where K=1])
lift_definition cblinfun_right :: 'b::complex_normed_vector CL ('a::complex_normed_vector×'b) is (λx. (0,x))
  by (auto intro!: bounded_clinearI[where K=1])

lemma isometry_cblinfun_left[simp]: isometry cblinfun_left
  apply (rule orthogonal_on_basis_is_isometry[of some_chilbert_basis])
   apply simp
  by transfer simp

lemma isometry_cblinfun_right[simp]: isometry cblinfun_right
  apply (rule orthogonal_on_basis_is_isometry[of some_chilbert_basis])
   apply simp
  by transfer simp

lemma cblinfun_left_right_ortho[simp]: cblinfun_left* oCL cblinfun_right = 0
proof -
  have x C ((cblinfun_left* oCL cblinfun_right) *V y) = 0 for x :: 'b and y :: 'a
    apply (simp add: cinner_adj_right)
    by transfer auto
  then show ?thesis
    by (metis cblinfun.zero_left cblinfun_eqI cinner_eq_zero_iff)
qed

lemma cblinfun_right_left_ortho[simp]: cblinfun_right* oCL cblinfun_left = 0
proof -
  have x C ((cblinfun_right* oCL cblinfun_left) *V y) = 0 for x :: 'b and y :: 'a
    apply (simp add: cinner_adj_right)
    by transfer auto
  then show ?thesis
    by (metis cblinfun.zero_left cblinfun_eqI cinner_eq_zero_iff)
qed

lemma cblinfun_left_apply[simp]: cblinfun_left *V ψ = (ψ,0)
  by transfer simp

lemma cblinfun_left_adj_apply[simp]: cblinfun_left* *V ψ = fst ψ
  apply (cases ψ)
  by (auto intro!: cinner_extensionality[of _ *V _] simp: cinner_adj_right)

lemma cblinfun_right_apply[simp]: cblinfun_right *V ψ = (0,ψ)
  by transfer simp

lemma cblinfun_right_adj_apply[simp]: cblinfun_right* *V ψ = snd ψ
  apply (cases ψ)
  by (auto intro!: cinner_extensionality[of _ *V _] simp: cinner_adj_right)

lift_definition ccsubspace_Times :: 'a::complex_normed_vector ccsubspace  'b::complex_normed_vector ccsubspace  ('a×'b) ccsubspace is
  Product_Type.Times
proof -
  fix S :: 'a set and T :: 'b set
  assume [simp]: closed_csubspace S closed_csubspace T
  have csubspace (S × T)
    by (simp add: complex_vector.subspace_Times)
  moreover have closed (S × T)
    by (simp add: closed_Times closed_csubspace.closed)
  ultimately show closed_csubspace (S × T)
    by (rule closed_csubspace.intro)
qed


lemma ccspan_Times: ccspan (S × T) = ccsubspace_Times (ccspan S) (ccspan T) if 0  S and 0  T
proof (transfer fixing: S T)
  from that have closure (cspan (S × T)) = closure (cspan S × cspan T)
    by (simp add: cspan_Times)
  also have  = closure (cspan S) × closure (cspan T)
    using closure_Times by blast
  finally   show closure (cspan (S × T)) = closure (cspan S) × closure (cspan T)
    by -
qed

lemma ccspan_Times_sing1: ccspan ({0::'a::complex_normed_vector} × B) = ccsubspace_Times 0 (ccspan B)
proof (transfer fixing: B)
  have closure (cspan ({0::'a} × B)) = closure ({0} × cspan B)
    by (simp add: complex_vector.span_Times_sing1)
  also have  = closure {0} × closure (cspan B)
    using closure_Times by blast
  also have  = {0} × closure (cspan B)
    by simp
  finally show closure (cspan ({0::'a} × B)) = {0} × closure (cspan B)
    by -
qed

lemma ccspan_Times_sing2: ccspan (B × {0::'a::complex_normed_vector}) = ccsubspace_Times (ccspan B) 0
proof (transfer fixing: B)
  have closure (cspan (B × {0::'a})) = closure (cspan B × {0})
    by (simp add: complex_vector.span_Times_sing2)
  also have  = closure (cspan B) × closure {0}
    using closure_Times by blast
  also have  = closure (cspan B) × {0}
    by simp
  finally show closure (cspan (B × {0::'a})) = closure (cspan B) × {0}
    by -
qed

lemma ccsubspace_Times_sup: sup (ccsubspace_Times A B) (ccsubspace_Times C D) = ccsubspace_Times (sup A C) (sup B D)
proof transfer
  fix A C :: 'a set and B D :: 'b set
  have A × B +M C × D = closure ((A × B) + (C × D))
    using closed_sum_def by blast
  also have  = closure ((A + C) × (B + D))
    by (simp add: set_Times_plus_distrib)
  also have  = closure (A + C) × closure (B + D)
    by (simp add: closure_Times)
  also have  = (A +M C) × (B +M D)
    by (simp add: closed_sum_def)
  finally show A × B +M C × D = (A +M C) × (B +M D)
    by -
qed

lemma ccsubspace_Times_top_top[simp]: ccsubspace_Times top top = top
  by transfer simp

lemma is_ortho_set_prod:
  assumes is_ortho_set B is_ortho_set B'
  shows is_ortho_set ((B × {0})  ({0} × B'))
  using assms unfolding is_ortho_set_def
  apply (auto simp: is_onb_def is_ortho_set_def zero_prod_def)
  by (meson is_onb_def is_ortho_set_def)+

lemma ccsubspace_Times_ccspan:
  assumes ccspan B = S and ccspan B' = S'
  shows ccspan ((B × {0})  ({0} × B')) = ccsubspace_Times S S'
  by (smt (z3) Diff_eq_empty_iff Sigma_cong assms(1) assms(2) ccspan.rep_eq ccspan_0 ccspan_Times_sing1 ccspan_Times_sing2 ccspan_of_empty ccspan_remove_0 ccspan_superset ccspan_union ccsubspace_Times_sup complex_vector.span_insert_0 space_as_set_bot sup_bot_left sup_bot_right)

lemma is_onb_prod:
  assumes is_onb B is_onb B'
  shows is_onb ((B × {0})  ({0} × B'))
  using assms by (auto intro!: is_ortho_set_prod simp add: is_onb_def ccsubspace_Times_ccspan)

subsection ‹Images›

text ‹The following definition defines the image of a closed subspace termS under a bounded operator termA.
We do not define that image as the image of termA seen as a function (termA ` S) but as the topological closure of that image.
This is because termA ` S might in general not be closed.

For example, if $e_i$ ($i\in\mathbb N$) form an orthonormal basis, and $A$ maps $e_i$ to $e_i/i$, 
then all $e_i$ are in termA ` S, so the closure of termA ` S is the whole space.
However, $\sum_i e_i/i$ is not in termA ` S because its preimage would have to be $\sum_i e_i$ which does not converge.
So termA ` S does not contain the whole space, hence it is not closed.›

lift_definition cblinfun_image :: 'a::complex_normed_vector CL 'b::complex_normed_vector
 'a ccsubspace  'b ccsubspace  (infixr "*S" 70)
  is "λA S. closure (A ` S)"
  using  bounded_clinear_def closed_closure  closed_csubspace.intro
  by (simp add: bounded_clinear_def complex_vector.linear_subspace_image closure_is_closed_csubspace)

lemma cblinfun_image_mono:
  assumes a1: "S  T"
  shows "A *S S  A *S T"
  using a1
  by (simp add: cblinfun_image.rep_eq closure_mono image_mono less_eq_ccsubspace.rep_eq)

lemma cblinfun_image_0[simp]:
  shows "U *S 0 = 0"
  thm zero_ccsubspace_def
  by transfer (simp add: bounded_clinear_def complex_vector.linear_0)

lemma cblinfun_image_bot[simp]: "U *S bot = bot"
  using cblinfun_image_0 by auto

lemma cblinfun_image_sup[simp]:
  fixes A B :: 'a::chilbert_space ccsubspace and U :: "'a CL'b::chilbert_space"
  shows U *S (sup A B) = sup (U *S A) (U *S B)
  apply transfer using bounded_clinear.bounded_linear closure_image_closed_sum by blast

lemma scaleC_cblinfun_image[simp]:
  fixes A :: 'a::chilbert_space CL 'b :: chilbert_space
    and S :: 'a ccsubspace and α :: complex
  shows (α *C A) *S S  = α *C (A *S S)
proof-
  have closure ( ( ((*C) α)  (cblinfun_apply A) ) ` space_as_set S) =
   ((*C) α) ` (closure (cblinfun_apply A ` space_as_set S))
    by (metis closure_scaleC image_comp)
  hence (closure (cblinfun_apply (α *C A) ` space_as_set S)) =
   ((*C) α) ` (closure (cblinfun_apply A ` space_as_set S))
    by (metis (mono_tags, lifting) comp_apply image_cong scaleC_cblinfun.rep_eq)
  hence Abs_ccsubspace (closure (cblinfun_apply (α *C A) ` space_as_set S)) =
            α *C Abs_ccsubspace (closure (cblinfun_apply A ` space_as_set S))
    by (metis space_as_set_inverse cblinfun_image.rep_eq scaleC_ccsubspace.rep_eq)
  have x1: "Abs_ccsubspace (closure ((*V) (α *C A) ` space_as_set S)) =
            α *C Abs_ccsubspace (closure ((*V) A ` space_as_set S))"
    using Abs_ccsubspace (closure (cblinfun_apply (α *C A) ` space_as_set S)) =
            α *C Abs_ccsubspace (closure (cblinfun_apply A ` space_as_set S))
    by blast
  show ?thesis
    unfolding cblinfun_image_def using x1 by force
qed

lemma cblinfun_image_id[simp]:
  "id_cblinfun *S ψ = ψ"
  by transfer (simp add: closed_csubspace.closed)

lemma cblinfun_compose_image:
  (A oCL B) *S S =  A *S (B *S S)
  apply transfer unfolding image_comp[symmetric]
  apply (rule closure_bounded_linear_image_subset_eq[symmetric])
  by (simp add: bounded_clinear.bounded_linear)

lemmas cblinfun_assoc_left = cblinfun_compose_assoc[symmetric] cblinfun_compose_image[symmetric]
  add.assoc[where ?'a="'a::chilbert_space CL 'b::chilbert_space", symmetric]
lemmas cblinfun_assoc_right = cblinfun_compose_assoc cblinfun_compose_image
  add.assoc[where ?'a="'a::chilbert_space CL 'b::chilbert_space"]

lemma cblinfun_image_INF_leq[simp]:
  fixes U :: "'b::complex_normed_vector CL 'c::complex_normed_vector"
    and V :: "'a  'b ccsubspace"
  shows U *S (INF iX. V i)  (INF iX. U *S (V i))
  apply transfer
  by (simp add: INT_greatest Inter_lower closure_mono image_mono)

lemma isometry_cblinfun_image_inf_distrib':
  fixes U::'a::complex_normed_vector CL 'b::cbanach and B C::"'a ccsubspace"
  shows "U *S (inf B C)  inf (U *S B) (U *S C)"
proof -
  define V where V b = (if b then B else C) for b
  have U *S (INF i. V i)  (INF i. U *S (V i))
    by auto
  then show ?thesis
    unfolding V_def
    by (metis (mono_tags, lifting) INF_UNIV_bool_expand)
qed

lemma cblinfun_image_eq:
  fixes S :: "'a::cbanach ccsubspace"
    and A B :: "'a::cbanach CL'b::cbanach"
  assumes "x. x  G  A *V x = B *V x" and "ccspan G  S"
  shows "A *S S = B *S S"
proof (use assms in transfer)
  fix G :: "'a set" and A :: "'a  'b" and B :: "'a  'b" and S :: "'a set"
  assume a1: "bounded_clinear A"
  assume a2: "bounded_clinear B"
  assume a3: "x. x  G  A x = B x"
  assume a4: "S  closure (cspan G)"

  have "A ` closure S = B ` closure S"
    by (smt (verit, best) UnCI a1 a2 a3 a4 bounded_clinear_eq_on_closure closure_Un closure_closure image_cong sup.absorb_iff1)
  then show "closure (A ` S) = closure (B ` S)"
    by (metis bounded_clinear.bounded_linear a1 a2 closure_bounded_linear_image_subset_eq)
qed

lemma cblinfun_fixes_range:
  assumes "A oCL B = B" and "ψ  space_as_set (B *S top)"
  shows "A *V ψ = ψ"
proof-
  define rangeB rangeB' where "rangeB = space_as_set (B *S top)"
    and "rangeB' = range (cblinfun_apply B)"
  from assms have "ψ  closure rangeB'"
    by (simp add: cblinfun_image.rep_eq rangeB'_def top_ccsubspace.rep_eq)

  then obtain ψi where ψi_lim: "ψi  ψ" and ψi_B: "ψi i  rangeB'" for i
    using closure_sequential by blast
  have A_invariant: "A *V ψi i = ψi i"
    for i
  proof-
    from ψi_B obtain φ where φ: "ψi i = B *V φ"
      using rangeB'_def by blast
    hence "A *V ψi i = (A oCL B) *V φ"
      by (simp add: cblinfun_compose.rep_eq)
    also have " = B *V φ"
      by (simp add: assms)
    also have " = ψi i"
      by (simp add: φ)
    finally show ?thesis.
  qed
  from ψi_lim have "(λi. A *V (ψi i))  A *V ψ"
    by (rule isCont_tendsto_compose[rotated], simp)
  with A_invariant have "(λi. ψi i)  A *V ψ"
    by auto
  with ψi_lim show "A *V ψ = ψ"
    using LIMSEQ_unique by blast
qed

lemma zero_cblinfun_image[simp]: "0 *S S = (0::_ ccsubspace)"
  by transfer (simp add: complex_vector.subspace_0 image_constant[where x=0])

lemma cblinfun_image_INF_eq_general:
  fixes V :: "'a  'b::chilbert_space ccsubspace"
    and U :: "'b CL'c::chilbert_space"
    and Uinv :: "'c CL'b"
  assumes UinvUUinv: "Uinv oCL U oCL Uinv = Uinv" and UUinvU: "U oCL Uinv oCL U = U"
    ― ‹Meaning: termUinv is a Pseudoinverse of termU
    and V: "i. V i  Uinv *S top"
    and X  {}
  shows "U *S (INF iX. V i) = (INF iX. U *S V i)"
proof (rule antisym)
  show "U *S (INF iX. V i)  (INF iX. U *S V i)"
    by (rule cblinfun_image_INF_leq)
next
  define rangeU rangeUinv where "rangeU = U *S top" and "rangeUinv = Uinv *S top"
  define INFUV INFV where INFUV_def: "INFUV = (INF iX. U *S V i)" and INFV_def: "INFV = (INF iX. V i)"
  from assms have "V i  rangeUinv"
    for i
    unfolding rangeUinv_def by simp
  moreover have "(Uinv oCL U) *V ψ = ψ" if "ψ  space_as_set rangeUinv"
    for ψ
    using UinvUUinv cblinfun_fixes_range rangeUinv_def that by fastforce
  ultimately have "(Uinv oCL U) *V ψ = ψ" if "ψ  space_as_set (V i)"
    for ψ i
    using less_eq_ccsubspace.rep_eq that by blast
  hence d1: "(Uinv oCL U) *S (V i) = (V i)" for i
  proof (transfer fixing: i)
    fix V :: "'a  'b set"
      and Uinv :: "'c  'b"
      and U :: "'b  'c"
    assume "pred_fun  closed_csubspace V"
      and "bounded_clinear Uinv"
      and "bounded_clinear U"
      and "ψ i. ψ  V i  (Uinv  U) ψ = ψ"
    then show "closure ((Uinv  U) ` V i) = V i"
    proof auto
      fix x
      from pred_fun  closed_csubspace V
      show "x  V i"
        if "x  closure (V i)" 
        using that apply simp
        by (metis orthogonal_complement_of_closure closed_csubspace.subspace double_orthogonal_complement_id closure_is_closed_csubspace)
      with pred_fun  closed_csubspace V
      show "x  closure (V i)"
        if "x  V i"
        using that
        using setdist_eq_0_sing_1 setdist_sing_in_set
        by blast
    qed
  qed
  have "U *S V i  rangeU" for i
    by (simp add: cblinfun_image_mono rangeU_def)
  hence "INFUV  rangeU"
    unfolding INFUV_def using X  {}
    by (metis INF_eq_const INF_lower2)
  moreover have "(U oCL Uinv) *V ψ = ψ" if "ψ  space_as_set rangeU" for ψ
    using UUinvU cblinfun_fixes_range rangeU_def that by fastforce
  ultimately have x: "(U oCL Uinv) *V ψ = ψ" if "ψ  space_as_set INFUV" for ψ
    by (simp add: in_mono less_eq_ccsubspace.rep_eq that)

  have "closure ((U  Uinv) ` INFUV) = INFUV"
    if "closed_csubspace INFUV"
      and "bounded_clinear U"
      and "bounded_clinear Uinv"
      and "ψ. ψ  INFUV  (U  Uinv) ψ = ψ"
    for INFUV :: "'c set"
    using that
  proof auto
    fix x
    show "x  INFUV" if "x  closure INFUV"
      using that closed_csubspace INFUV
      by (metis orthogonal_complement_of_closure closed_csubspace.subspace double_orthogonal_complement_id closure_is_closed_csubspace)
    show "x  closure INFUV"
      if "x  INFUV"
      using that closed_csubspace INFUV
      using setdist_eq_0_sing_1 setdist_sing_in_set
      by (simp add: closed_csubspace.closed)
  qed
  hence "(U oCL Uinv) *S INFUV = INFUV"
    by (metis (mono_tags, opaque_lifting) x cblinfun_image.rep_eq cblinfun_image_id id_cblinfun_apply image_cong
        space_as_set_inject)
  hence "INFUV = U *S Uinv *S INFUV"
    by (simp add: cblinfun_compose_image)
  also have "  U *S (INF iX. Uinv *S U *S V i)"
    unfolding INFUV_def
    by (metis cblinfun_image_mono cblinfun_image_INF_leq)
  also have " = U *S INFV"
    using d1
    by (metis (no_types, lifting) INFV_def cblinfun_assoc_left(2) image_cong)
  finally show "INFUV  U *S INFV".
qed

lemma unitary_range[simp]:
  assumes "unitary U"
  shows "U *S top = top"
  using assms unfolding unitary_def by transfer (metis closure_UNIV comp_apply surj_def)

lemma range_adjoint_isometry:
  assumes "isometry U"
  shows "U* *S top = top"
proof-
  from assms have "top = U* *S U *S top"
    by (simp add: cblinfun_assoc_left(2))
  also have "  U* *S top"
    by (simp add: cblinfun_image_mono)
  finally show ?thesis
    using top.extremum_unique by blast
qed

lemma cblinfun_image_INF_eq[simp]:
  fixes V :: "'a  'b::chilbert_space ccsubspace"
    and U :: "'b CL 'c::chilbert_space"
  assumes isometry U X  {}
  shows "U *S (INF iX. V i) = (INF iX. U *S V i)"
proof -
  from isometry U have "U* oCL U oCL U* = U*"
    unfolding isometry_def by simp
  moreover from isometry U have "U oCL U* oCL U = U"
    unfolding isometry_def
    by (simp add: cblinfun_compose_assoc)
  moreover have "V i  U* *S top" for i
    by (simp add: range_adjoint_isometry assms)
  ultimately show ?thesis
    using X  {} by (rule cblinfun_image_INF_eq_general)
qed

lemma isometry_cblinfun_image_inf_distrib[simp]:
  fixes U::'a::chilbert_space CL 'b::chilbert_space
    and X Y::"'a ccsubspace"
  assumes "isometry U"
  shows "U *S (inf X Y) = inf (U *S X) (U *S Y)"
  using cblinfun_image_INF_eq[where V="λb. if b then X else Y" and U=U and X=UNIV]
  unfolding INF_UNIV_bool_expand
  using assms by auto

lemma cblinfun_image_ccspan:
  shows "A *S ccspan G = ccspan ((*V) A ` G)"
  by transfer (simp add: bounded_clinear.bounded_linear bounded_clinear_def closure_bounded_linear_image_subset_eq complex_vector.linear_span_image)

lemma cblinfun_apply_in_image[simp]: "A *V ψ  space_as_set (A *S )"
  by (metis cblinfun_image.rep_eq closure_subset in_mono range_eqI top_ccsubspace.rep_eq)


lemma cblinfun_plus_image_distr:
  (A + B) *S S  A *S S  B *S S
  by transfer (smt (verit, ccfv_threshold) closed_closure closed_sum_def closure_minimal closure_subset image_subset_iff set_plus_intro subset_eq)

lemma cblinfun_sum_image_distr:
  (iI. A i) *S S  (SUP iI. A i *S S)
proof (cases finite I)
  case True
  then show ?thesis
  proof induction
    case empty
    then show ?case
      by auto
  next
    case (insert x F)
    then show ?case
      by auto (smt (z3) cblinfun_plus_image_distr inf_sup_aci(6) le_iff_sup)
  qed
next
  case False
  then show ?thesis
    by auto
qed

lemma space_as_set_image_commute:
  assumes UV: U oCL V = id_cblinfun and VU: V oCL U = id_cblinfun
    (* I think only one of them is needed, can the lemma be strengthened? *)
  shows (*V) U ` space_as_set T = space_as_set (U *S T)
proof -
  have space_as_set (U *S T) = U ` V ` space_as_set (U *S T)
    by (simp add: image_image UV flip: cblinfun_apply_cblinfun_compose)
  also have   U ` space_as_set (V *S U *S T)
    by (metis cblinfun_image.rep_eq closure_subset image_mono)
  also have  = U ` space_as_set T
    by (simp add: VU cblinfun_assoc_left(2))
  finally have 1: space_as_set (U *S T)  U ` space_as_set T
    by -
  have 2: U ` space_as_set T  space_as_set (U *S T)
    by (simp add: cblinfun_image.rep_eq closure_subset)
  from 1 2 show ?thesis
    by simp
qed

lemma right_total_rel_ccsubspace:
  fixes R :: 'a::complex_normed_vector  'b::complex_normed_vector  bool
  assumes UV: U oCL V = id_cblinfun
  assumes VU: V oCL U = id_cblinfun
  assumes R_def: x y. R x y  x = U *V y
  shows right_total (rel_ccsubspace R)
proof (rule right_totalI)
  fix T :: 'b ccsubspace
  show S. rel_ccsubspace R S T
    apply (rule exI[of _ U *S T])
    using UV VU by (auto simp add: rel_ccsubspace_def R_def rel_set_def simp flip: space_as_set_image_commute)
qed

lemma left_total_rel_ccsubspace:
  fixes R :: 'a::complex_normed_vector  'b::complex_normed_vector  bool
  assumes UV: U oCL V = id_cblinfun
  assumes VU: V oCL U = id_cblinfun
  assumes R_def: x y. R x y  y = U *V x
  shows left_total (rel_ccsubspace R)
proof -
  have right_total (rel_ccsubspace (conversep R))
    apply (rule right_total_rel_ccsubspace)
    using assms by auto
  then show ?thesis
    by (auto intro!: right_total_conversep[THEN iffD1] simp: converse_rel_ccsubspace)
qed

lemma cblinfun_image_bot_zero[simp]: A *S top = bot  A = 0
  by (metis Complex_Bounded_Linear_Function.zero_cblinfun_image bot_ccsubspace.rep_eq cblinfun_apply_in_image cblinfun_eqI empty_iff insert_iff zero_ccsubspace_def)

lemma surj_isometry_is_unitary:
  ― ‹This lemma is a bit stronger than its name suggests:
      We actually only require that the image of U is dense.

      The converse is @{thm [source] unitary_surj}
  fixes U :: 'a::chilbert_space CL 'b::chilbert_space
  assumes isometry U
  assumes U *S  = 
  shows unitary U
  by (metis UNIV_I assms(1) assms(2) cblinfun_assoc_left(1) cblinfun_compose_id_right cblinfun_eqI cblinfun_fixes_range id_cblinfun_apply isometry_def space_as_set_top unitary_def)

lemma cblinfun_apply_in_image': "A *V ψ  space_as_set (A *S S)" if ψ  space_as_set S
  by (metis cblinfun_image.rep_eq closure_subset image_subset_iff that)

lemma cblinfun_image_ccspan_leqI:
  assumes v. v  M  A *V v  space_as_set T
  shows A *S ccspan M  T
  by (simp add: assms cblinfun_image_ccspan ccspan_leqI image_subsetI)


lemma cblinfun_same_on_image: A ψ = B ψ if eq: A oCL C = B oCL C and mem: ψ  space_as_set (C *S )
proof -
  have A ψ = B ψ if ψ  range C for ψ
    by (metis (no_types, lifting) eq cblinfun_apply_cblinfun_compose image_iff that)
  moreover have ψ  closure (range C)
    by (metis cblinfun_image.rep_eq mem top_ccsubspace.rep_eq)
  ultimately show ?thesis
    apply (rule on_closure_eqI)
    by (auto simp: continuous_on_eq_continuous_at)
qed

lemma lift_cblinfun_comp:
― ‹Utility lemma to lift a lemma of the form terma oCL b = c
   to become a more general rewrite rule.›
  assumes a oCL b = c
  shows a oCL b = c
    and a oCL (b oCL d) = c oCL d
    and a *S (b *S S) = c *S S
    and a *V (b *V x) = c *V x
     apply (fact assms)
    apply (simp add: assms cblinfun_assoc_left(1))
  using assms cblinfun_assoc_left(2) apply force
  using assms by force

lemma cblinfun_image_def2: A *S S = ccspan ((*V) A ` space_as_set S)
  apply (simp add: flip: cblinfun_image_ccspan)
  by (metis ccspan_leqI ccspan_superset less_eq_ccsubspace.rep_eq order_class.order_eq_iff)

lemma unitary_image_onb:
  ― ‹Logically belongs in an earlier section but the proof uses results from this section.›
  assumes is_onb A
  assumes unitary U
  shows is_onb (U ` A)
  using assms
  by (auto simp add: is_onb_def isometry_image_is_ortho_set isometry_preserves_norm
      simp flip: cblinfun_image_ccspan)

subsection ‹Sandwiches›

lift_definition sandwich :: ('a::chilbert_space CL 'b::complex_inner)  (('a CL 'a) CL ('b CL 'b)) is
  λ(A::'aCL'b) B. A oCL B oCL A*
proof
  fix A :: 'a CL 'b and B B1 B2 :: 'a CL 'a and c :: complex
  show A oCL (B1 + B2) oCL A* = (A oCL B1 oCL A*) + (A oCL B2 oCL A*)
    by (simp add: cblinfun_compose_add_left cblinfun_compose_add_right)
  show A oCL (c *C B) oCL A* = c *C (A oCL B oCL A*)
    by auto
  show K. B. norm (A oCL B oCL A*)  norm B * K
  proof (rule exI[of _ norm A * norm (A*)], rule allI)
    fix B
    have norm (A oCL B oCL A*)  norm (A oCL B) * norm (A*)
      using norm_cblinfun_compose by blast
    also have   (norm A * norm B) * norm (A*)
      by (simp add: mult_right_mono norm_cblinfun_compose)
    finally show norm (A oCL B oCL A*)  norm B * (norm A * norm (A*))
      by (simp add: mult.assoc vector_space_over_itself.scale_left_commute)
  qed
qed

lemma sandwich_0[simp]: sandwich 0 = 0
  by (simp add: cblinfun_eqI sandwich.rep_eq)

lemma sandwich_apply: sandwich A *V B = A oCL B oCL A*
  apply (transfer fixing: A B) by auto

lemma sandwich_arg_compose:
  assumes isometry U
  shows sandwich U x oCL sandwich U y = sandwich U (x oCL y)
  apply (simp add: sandwich_apply)
  by (metis (no_types, lifting) lift_cblinfun_comp(2) assms cblinfun_compose_id_right isometryD)

lemma norm_sandwich: norm (sandwich A) = (norm A)2 for A :: 'a::{chilbert_space} CL 'b::{complex_inner}
proof -
  have main: norm (sandwich A) = (norm A)2 for A :: 'c::{chilbert_space,not_singleton} CL 'd::{complex_inner}
  proof (rule norm_cblinfun_eqI)
    show (norm A)2  norm (sandwich A *V id_cblinfun) / norm (id_cblinfun :: 'c CL _)
      apply (auto simp: sandwich_apply)
      by -
    fix B
    have norm (sandwich A *V B)  norm (A oCL B) * norm (A*)
      using norm_cblinfun_compose by (auto simp: sandwich_apply simp del: norm_adj)
    also have   (norm A * norm B) * norm (A*)
      by (simp add: mult_right_mono norm_cblinfun_compose)
    also have   (norm A)2 * norm B
      by (simp add: power2_eq_square mult.assoc vector_space_over_itself.scale_left_commute)
    finally show norm (sandwich A *V B)  (norm A)2 * norm B
      by -
    show 0  (norm A)2
      by auto
  qed

  show ?thesis
  proof (cases class.not_singleton TYPE('a))
    case True
    show ?thesis
      apply (rule main[internalize_sort' 'c2])
       apply standard[1]
      using True by simp
  next
    case False
    have A = 0
      apply (rule cblinfun_from_CARD_1_0[internalize_sort' 'a])
       apply (rule not_singleton_vs_CARD_1)
       apply (rule False)
      by standard
    then show ?thesis
      by simp
  qed
qed

lemma sandwich_apply_adj: sandwich A (B*) = (sandwich A B)*
  by (simp add: cblinfun_assoc_left(1) sandwich_apply)

lemma sandwich_id[simp]: "sandwich id_cblinfun = id_cblinfun"
  apply (rule cblinfun_eqI)
  by (auto simp: sandwich_apply)

lemma sandwich_compose: sandwich (A oCL B) = sandwich A oCL sandwich B
  by (auto intro!: cblinfun_eqI simp: sandwich_apply)

lemma inj_sandwich_isometry: inj (sandwich U) if [simp]: isometry U for U :: 'a::chilbert_space CL 'b::chilbert_space
  apply (rule inj_on_inverseI[where g=(*V) (sandwich (U*))])
  by (auto simp flip: cblinfun_apply_cblinfun_compose sandwich_compose)

lemma sandwich_isometry_id: isometry (U*)  sandwich U id_cblinfun = id_cblinfun
  by (simp add: sandwich_apply isometry_def)


subsection ‹Projectors›

lift_definition Proj :: "('a::chilbert_space) ccsubspace  'a CL'a"
  is projection
  by (rule projection_bounded_clinear)

lemma Proj_range[simp]: "Proj S *S top = S"
proof transfer
  fix S :: 'a set assume closed_csubspace S
  then have "closure (range (projection S))  S"
    by (metis closed_csubspace.closed closed_csubspace.subspace closure_closed complex_vector.subspace_0 csubspace_is_convex dual_order.eq_iff insert_absorb insert_not_empty projection_image)
  moreover have "S  closure (range (projection S))"
    using closed_csubspace S
    by (metis closed_csubspace_def closure_subset csubspace_is_convex equals0D projection_image subset_iff)
  ultimately show closure (range (projection S)) = S
    by auto
qed

lemma adj_Proj: (Proj M)* = Proj M
  by transfer (simp add: projection_cadjoint)

lemma Proj_idempotent[simp]: Proj M oCL Proj M = Proj M
proof -
  have u1: (cblinfun_apply (Proj M)) = projection (space_as_set M)
    by transfer blast
  have closed_csubspace (space_as_set M)
    using space_as_set by auto
  hence u2: (projection (space_as_set M))(projection (space_as_set M))
                = (projection (space_as_set M))
    using projection_idem by fastforce
  have (cblinfun_apply (Proj M))  (cblinfun_apply (Proj M)) = cblinfun_apply (Proj M)
    using u1 u2
    by simp
  hence cblinfun_apply ((Proj M) oCL (Proj M)) = cblinfun_apply (Proj M)
    by (simp add: cblinfun_compose.rep_eq)
  thus ?thesis using cblinfun_apply_inject
    by auto
qed

lift_definition is_Proj :: 'a::complex_normed_vector CL 'a  bool is
  λP. M. is_projection_on P M .

lemma is_Proj_id[simp]: is_Proj id_cblinfun
  apply transfer
  by (auto intro!: exI[of _ UNIV] simp: is_projection_on_def is_arg_min_def)

lemma Proj_top[simp]: Proj  = id_cblinfun
  by (metis Proj_idempotent Proj_range cblinfun_eqI cblinfun_fixes_range id_cblinfun_apply iso_tuple_UNIV_I space_as_set_top)

lemma Proj_on_own_range':
  fixes P :: 'a::chilbert_space CL'a
  assumes P oCL P = P and P = P*
  shows Proj (P *S top) = P
proof -
  define M where "M = P *S top"
  have v3: "x  (λx. x - P *V x) -` {0}"
    if "x  range (cblinfun_apply P)"
    for x :: 'a
  proof-
    have v3_1: cblinfun_apply P  cblinfun_apply P = cblinfun_apply P
      by (metis P oCL P = P cblinfun_compose.rep_eq)
    have t. P *V t = x
      using that by blast
    then obtain t where t_def: P *V t = x
      by blast
    hence x - P *V x = x - P *V (P *V t)
      by simp
    also have  = x - (P *V t)
      using v3_1
      by (metis comp_apply)
    also have  = 0
      by (simp add: t_def)
    finally have x - P *V x = 0
      by blast
    thus ?thesis
      by simp
  qed

  have v1: "range (cblinfun_apply P)  (λx. x - cblinfun_apply P x) -` {0}"
    using v3
    by blast

  have "x  range (cblinfun_apply P)"
    if "x  (λx. x - P *V x) -` {0}"
    for x :: 'a
  proof-
    have x1:x - P *V x = 0
      using that by blast
    have x = P *V x
      by (simp add: x1 eq_iff_diff_eq_0)
    thus ?thesis
      by blast
  qed
  hence v2: "(λx. x - cblinfun_apply P x) -` {0}  range (cblinfun_apply P)"
    by blast
  have i1: range (cblinfun_apply P) = (λ x. x - cblinfun_apply P x) -` {0}
    using v1 v2
    by (simp add: v1 dual_order.antisym)
  have p1: closed {(0::'a)}
    by simp
  have p2: continuous (at x) (λ x. x - P *V x)
    for x
  proof-
    have cblinfun_apply (id_cblinfun - P) = (λ x. x - P *V x)
      by (simp add: id_cblinfun.rep_eq minus_cblinfun.rep_eq)
    hence bounded_clinear (cblinfun_apply (id_cblinfun - P))
      using cblinfun_apply
      by blast
    hence continuous (at x) (cblinfun_apply (id_cblinfun - P))
      by (simp add: clinear_continuous_at)
    thus ?thesis
      using cblinfun_apply (id_cblinfun - P) = (λ x. x - P *V x)
      by simp
  qed

  have i2: closed ( (λ x. x - P *V x) -` {0} )
    using p1 p2
    by (rule Abstract_Topology.continuous_closed_vimage)

  have closed (range (cblinfun_apply P))
    using i1 i2
    by simp
  have u2: cblinfun_apply P x  space_as_set M
    for x
    by (simp add: M_def closed (range ((*V) P)) cblinfun_image.rep_eq top_ccsubspace.rep_eq)

  have xy: is_orthogonal (x - P *V x) y
    if y1: y  space_as_set M
    for x y
  proof-
    have t. y = P *V t
      using y1
      by (simp add:  M_def closed (range ((*V) P)) cblinfun_image.rep_eq image_iff
          top_ccsubspace.rep_eq)
    then obtain t where t_def: y = P *V t
      by blast
    have (x - P *V x) C y = (x - P *V x) C (P *V t)
      by (simp add: t_def)
    also have  = (P *V (x - P *V x)) C t
      by (metis P = P* cinner_adj_left)
    also have  = (P *V x - P *V (P *V x)) C t
      by (simp add: cblinfun.diff_right)
    also have  = (P *V x - P *V x) C t
      by (metis assms(1) comp_apply cblinfun_compose.rep_eq)
    also have  = (0 C t)
      by simp
    also have  = 0
      by simp
    finally show ?thesis by blast
  qed
  hence u1: x - P *V x  orthogonal_complement (space_as_set M)
    for x
    by (simp add: orthogonal_complementI)
  have "closed_csubspace (space_as_set M)"
    using space_as_set by auto
  hence f1: "(Proj M) *V a = P *V a" for a
    by (simp add: Proj.rep_eq projection_eqI u1 u2)
  have "(+) ((P - Proj M) *V a) = id" for a
    using f1
    by (auto intro!: ext simp add: minus_cblinfun.rep_eq)
  hence "b - b = cblinfun_apply (P - Proj M) a"
    for a b
    by (metis (no_types) add_diff_cancel_right' id_apply)
  hence "cblinfun_apply (id_cblinfun - (P - Proj M)) a = a"
    for a
    by (simp add: minus_cblinfun.rep_eq)
  thus ?thesis
    using u1 u2 cblinfun_apply_inject diff_diff_eq2 diff_eq_diff_eq eq_id_iff id_cblinfun.rep_eq
    by (metis (no_types, opaque_lifting) M_def)
qed

lemma Proj_range_closed:
  assumes "is_Proj P"
  shows "closed (range (cblinfun_apply P))"
  apply (rule is_projection_on_closed[where f=cblinfun_apply P])
  using assms is_Proj.rep_eq is_projection_on_image by auto

lemma Proj_is_Proj[simp]:
  fixes M::'a::chilbert_space ccsubspace
  shows is_Proj (Proj M)
proof-
  have u1: "closed_csubspace (space_as_set M)"
    using space_as_set by blast
  have v1: "h - Proj M *V h
          orthogonal_complement (space_as_set M)" for h
    by (simp add: Proj.rep_eq orthogonal_complementI projection_orthogonal u1)
  have v2: "Proj M *V h  space_as_set M" for h
    by (metis Proj.rep_eq mem_Collect_eq orthog_proj_exists projection_eqI space_as_set)
  have u2: "is_projection_on ((*V) (Proj M)) (space_as_set M)"
    unfolding is_projection_on_def
    by (simp add: smallest_dist_is_ortho u1 v1 v2)
  show ?thesis
    using u1 u2 is_Proj.rep_eq by blast
qed

lemma is_Proj_algebraic:
  fixes P::'a::chilbert_space CL 'a
  shows is_Proj P  P oCL P = P  P = P*
proof
  have "P oCL P = P"
    if "is_Proj P"
    using that apply transfer
    using is_projection_on_idem
    by fastforce
  moreover have "P = P*"
    if "is_Proj P"
    using that Proj_range_closed[OF that] is_projection_on_cadjoint[where π=P and M=range P]
    by transfer (metis bounded_clinear.axioms(1) closed_csubspace_UNIV closed_csubspace_def complex_vector.linear_subspace_image is_projection_on_image)
  ultimately show "P oCL P = P  P = P*"
    if "is_Proj P"
    using that
    by blast
  show "is_Proj P"
    if "P oCL P = P  P = P*"
    using that Proj_on_own_range' Proj_is_Proj by metis
qed

lemma Proj_on_own_range:
  fixes P :: 'a::chilbert_space CL'a
  assumes is_Proj P
  shows Proj (P *S top) = P
  using Proj_on_own_range' assms is_Proj_algebraic by blast

lemma Proj_image_leq: "(Proj S) *S A  S"
  by (metis Proj_range inf_top_left le_inf_iff isometry_cblinfun_image_inf_distrib')

lemma Proj_sandwich:
  fixes A::"'a::chilbert_space CL 'b::chilbert_space"
  assumes "isometry A"
  shows "sandwich A *V Proj S = Proj (A *S S)"
proof -
  define P where P = A oCL Proj S oCL (A*)
  have P oCL P = P
    using assms
    unfolding P_def isometry_def
    by (metis (no_types, lifting) Proj_idempotent cblinfun_assoc_left(1) cblinfun_compose_id_left)
  moreover have P = P*
    unfolding P_def
    by (metis adj_Proj adj_cblinfun_compose cblinfun_assoc_left(1) double_adj)
  ultimately have
    M. P = Proj M  space_as_set M = range (cblinfun_apply (A oCL (Proj S) oCL (A*)))
    using P_def Proj_on_own_range'
    by (metis Proj_is_Proj Proj_range_closed cblinfun_image.rep_eq closure_closed top_ccsubspace.rep_eq)
  then obtain M where P = Proj M
    and space_as_set M = range (cblinfun_apply (A oCL (Proj S) oCL (A*)))
    by blast

  have f1: "A oCL Proj S = P oCL A"
    by (simp add: P_def assms cblinfun_compose_assoc)
  hence "P oCL A oCL A* = P"
    using P_def by presburger
  hence "(P oCL A) *S (c  A* *S d) = P *S (A *S c  d)"
    for c d

    by (simp add: cblinfun_assoc_left(2))
  hence "P *S (A *S   c) = (P oCL A) *S "
    for c
    by (metis sup_top_left)
  hence M = A *S S
    using f1
    by (metis P = Proj M cblinfun_assoc_left(2) Proj_range sup_top_right)
  thus ?thesis
    using P = Proj M
    unfolding P_def sandwich_apply by blast
qed

lemma Proj_orthog_ccspan_union:
  assumes "x y. x  X  y  Y  is_orthogonal x y"
  shows Proj (ccspan (X  Y)) = Proj (ccspan X) + Proj (ccspan Y)
proof -
  have x  cspan X  y  cspan Y  is_orthogonal x y for x y
    apply (rule is_orthogonal_closure_cspan[where X=X and Y=Y])
    using closure_subset assms by auto
  then have x  closure (cspan X)  y  closure (cspan Y)  is_orthogonal x y for x y
    by (metis orthogonal_complementI orthogonal_complement_of_closure orthogonal_complement_orthoI')
  then show ?thesis
    apply (transfer fixing: X Y)
    apply (subst projection_plus[symmetric])
    by auto
qed

abbreviation proj :: "'a::chilbert_space  'a CL 'a" where "proj ψ  Proj (ccspan {ψ})"

lemma proj_0[simp]: proj 0 = 0
  by transfer auto

lemma ccsubspace_supI_via_Proj:
  fixes A B C::"'a::chilbert_space ccsubspace"
  assumes a1: Proj (- C) *S A  B
  shows  "A  B  C"
proof-
  have x2: x  space_as_set B
    if "x   closure ( (projection (orthogonal_complement (space_as_set C))) ` space_as_set A)" for x
    using that
    by (metis Proj.rep_eq cblinfun_image.rep_eq assms less_eq_ccsubspace.rep_eq subsetD
        uminus_ccsubspace.rep_eq)
  have q1: x  closure {ψ + φ |ψ φ. ψ  space_as_set B  φ  space_as_set C}
    if x  space_as_set A
    for x
  proof-
    have p1: closed_csubspace (space_as_set C)
      using space_as_set by auto
    hence x = (projection (space_as_set C)) x
       + (projection (orthogonal_complement (space_as_set C))) x
      by simp
    hence x = (projection (orthogonal_complement (space_as_set C))) x
              + (projection (space_as_set C)) x
      by (metis ordered_field_class.sign_simps(2))
    moreover have (projection (orthogonal_complement (space_as_set C))) x  space_as_set B
      using x2
      by (meson closure_subset image_subset_iff that)
    moreover have (projection (space_as_set C)) x  space_as_set C
      by (metis mem_Collect_eq orthog_proj_exists projection_eqI space_as_set)
    ultimately show ?thesis
      using closure_subset by force
  qed
  have x1: x  (space_as_set B +M space_as_set C)
    if "x  space_as_set A" for x
  proof -
    have f1: "x  closure {a + b |a b. a  space_as_set B  b  space_as_set C}"
      by (simp add: q1 that)
    have "{a + b |a b. a  space_as_set B  b  space_as_set C} = {a. p. p  space_as_set B
       (q. q  space_as_set C  a = p + q)}"
      by blast
    hence "x  closure {a. bspace_as_set B. cspace_as_set C. a = b + c}"
      using f1 by (simp add: Bex_def_raw)
    thus ?thesis
      using that
      unfolding closed_sum_def set_plus_def
      by blast
  qed

  hence x  space_as_set (Abs_ccsubspace (space_as_set B +M space_as_set C))
    if "x  space_as_set A" for x
    using that
    by (metis space_as_set_inverse sup_ccsubspace.rep_eq)
  thus ?thesis
    by (simp add: x1 less_eq_ccsubspace.rep_eq subset_eq sup_ccsubspace.rep_eq)
qed

lemma is_Proj_idempotent:
  assumes "is_Proj P"
  shows "P oCL P = P"
  using assms apply transfer
  using is_projection_on_fixes_image is_projection_on_in_image by fastforce

lemma is_proj_selfadj:
  assumes "is_Proj P"
  shows "P* = P"
  using assms
  unfolding is_Proj_def
  by (metis is_Proj_algebraic is_Proj_def)

lemma is_Proj_I:
  assumes "P oCL P = P" and "P* = P"
  shows "is_Proj P"
  using assms is_Proj_algebraic by metis

lemma is_Proj_0[simp]: "is_Proj 0"
  apply transfer apply (rule exI[of _ 0])
  by (simp add: is_projection_on_zero)

lemma is_Proj_complement[simp]:
  fixes P :: 'a::chilbert_space CL 'a
  assumes a1: "is_Proj P"
  shows "is_Proj (id_cblinfun - P)"
  by (smt (z3) add_diff_cancel_left add_diff_cancel_left' adj_cblinfun_compose adj_plus assms bounded_cbilinear.add_left bounded_cbilinear_cblinfun_compose diff_add_cancel id_cblinfun_adjoint is_Proj_algebraic cblinfun_compose_id_left)

lemma Proj_bot[simp]: "Proj bot = 0"
  by (metis zero_cblinfun_image Proj_on_own_range' is_Proj_0 is_Proj_algebraic
      zero_ccsubspace_def)

lemma Proj_ortho_compl:
  "Proj (- X) = id_cblinfun - Proj X"
  by (transfer, auto)

lemma Proj_inj:
  assumes "Proj X = Proj Y"
  shows "X = Y"
  by (metis assms Proj_range)

lemma norm_Proj_leq1: norm (Proj M)  1 for M :: 'a :: chilbert_space ccsubspace
  by transfer (metis (no_types, opaque_lifting) mult.left_neutral onorm_bound projection_reduces_norm zero_less_one_class.zero_le_one)

lemma Proj_orthog_ccspan_insert:
  assumes "y. y  Y  is_orthogonal x y"
  shows Proj (ccspan (insert x Y)) = proj x + Proj (ccspan Y)
  apply (subst asm_rl[of insert x Y = {x}  Y], simp)
  apply (rule Proj_orthog_ccspan_union)
  using assms by auto

lemma Proj_fixes_image: Proj S *V ψ = ψ if ψ  space_as_set S
  by (metis Proj_idempotent Proj_range that cblinfun_fixes_range)

lemma norm_is_Proj: norm P  1 if is_Proj P for P :: 'a :: chilbert_space CL 'a
  by (metis Proj_on_own_range norm_Proj_leq1 that)

lemma Proj_sup: orthogonal_spaces S T  Proj (sup S T) = Proj S + Proj T
  unfolding orthogonal_spaces_def
  by transfer (simp add: projection_plus)

lemma Proj_sum_spaces:
  assumes finite X
  assumes x y. xX  yX  xy  orthogonal_spaces (J x) (J y)
  shows Proj (xX. J x) = (xX. Proj (J x))
  using assms
proof induction
  case empty
  show ?case 
    by auto
next
  case (insert x F)
  have Proj (sum J (insert x F)) = Proj (J x  sum J F)
    by (simp add: insert)
  also have  = Proj (J x) + Proj (sum J F)
    apply (rule Proj_sup)
    apply (rule orthogonal_sum)
    using insert by auto
  also have  = (xinsert x F. Proj (J x))
    by (simp add: insert.IH insert.hyps(1) insert.hyps(2) insert.prems)
  finally show ?case
    by -
qed

lemma is_Proj_reduces_norm:
  fixes P :: 'a::complex_inner CL 'a
  assumes is_Proj P
  shows norm (P *V ψ)  norm ψ
  apply (rule is_projection_on_reduces_norm[where M=range P])
  using assms is_Proj.rep_eq is_projection_on_image by blast (simp add: Proj_range_closed assms closed_csubspace.intro)

lemma norm_Proj_apply: norm (Proj T *V ψ) = norm ψ  ψ  space_as_set T
proof (rule iffI)
  show norm (Proj T *V ψ) = norm ψ if ψ  space_as_set T
    by (simp add: Proj_fixes_image that)
  assume assm: norm (Proj T *V ψ) = norm ψ
  have ψ_decomp: ψ = Proj T ψ + Proj (-T) ψ
    by (simp add: Proj_ortho_compl cblinfun.real.diff_left)
  have (norm (Proj (-T) ψ))2 = (norm (Proj T ψ))2 + (norm (Proj (-T) ψ))2 - (norm (Proj T ψ))2
    by auto
  also have  = (norm (Proj T ψ + Proj (-T) ψ))2 - (norm (Proj T ψ))2
    apply (subst pythagorean_theorem)
     apply (metis (no_types, lifting) Proj_idempotent ψ_decomp add_cancel_right_left adj_Proj cblinfun.real.add_right cblinfun_apply_cblinfun_compose cinner_adj_left cinner_zero_left)
    by simp
  also with ψ_decomp have  = (norm ψ)2 - (norm (Proj T ψ))2
    by metis
  also with assm have  = 0
    by simp
  finally have norm (Proj (-T) ψ) = 0
    by auto
  with ψ_decomp have ψ = Proj T ψ
    by auto
  then show ψ  space_as_set T
    by (metis Proj_range cblinfun_apply_in_image)
qed

lemma norm_Proj_apply_1: norm ψ = 1  norm (Proj T *V ψ) = 1  ψ  space_as_set T
  using norm_Proj_apply by metis

lemma norm_is_Proj_nonzero: norm P = 1 if is_Proj P and P  0 for P :: 'a::chilbert_space CL 'a
proof (rule antisym)
  show norm P  1
    by (simp add: norm_is_Proj that(1))
  from P  0
  have range P  0
    by (metis cblinfun_eq_0_on_UNIV_span complex_vector.span_UNIV rangeI set_zero singletonD)
  then obtain ψ where ψ  range P and ψ  0
    by force
  then have P ψ = ψ
    using is_Proj.rep_eq is_projection_on_fixes_image is_projection_on_image that(1) by blast
  then show norm P  1
    apply (rule_tac cblinfun_norm_geqI[of _ _ ψ])
    using ψ  0 by simp
qed


lemma Proj_compose_cancelI:
  assumes A *S   S
  shows Proj S oCL A = A
  apply (rule cblinfun_eqI)
proof -
  fix x
  have (Proj S oCL A) *V x = Proj S *V (A *V x)
  by simp
  also have  = A *V x
    apply (rule Proj_fixes_image)
    using assms cblinfun_apply_in_image less_eq_ccsubspace.rep_eq by blast
  finally show (Proj S oCL A) *V x = A *V x
    by -
qed

lemma space_as_setI_via_Proj:
  assumes Proj M *V x = x
  shows x  space_as_set M
  using assms norm_Proj_apply by fastforce

lemma unitary_image_ortho_compl: 
  ― ‹Logically, this lemma belongs in an earlier section but its proof uses projectors.›
  fixes U :: 'a::chilbert_space CL 'b::chilbert_space
  assumes [simp]: unitary U
  shows U *S (- A) = - (U *S A)
proof -
  have Proj (U *S (- A)) = sandwich U (Proj (- A))
    by (simp add: Proj_sandwich)
  also have  = sandwich U (id_cblinfun - Proj A)
    by (simp add: Proj_ortho_compl)
  also have  = id_cblinfun - sandwich U (Proj A)
    by (metis assms cblinfun.diff_right sandwich_isometry_id unitary_twosided_isometry)
  also have  = id_cblinfun - Proj (U *S A)
    by (simp add: Proj_sandwich)
  also have  = Proj (- (U *S A))
    by (simp add: Proj_ortho_compl)
  finally show ?thesis
    by (simp add: Proj_inj)
qed


lemma Proj_on_image [simp]: Proj S *S S = S
  by (metis Proj_idempotent Proj_range cblinfun_compose_image)

subsection ‹Kernel / eigenspaces›

lift_definition kernel :: "'a::complex_normed_vector CL 'b::complex_normed_vector
    'a ccsubspace"
  is "λf. f -` {0}"
  by (metis kernel_is_closed_csubspace)

definition eigenspace :: "complex  'a::complex_normed_vector CL'a  'a ccsubspace" where
  "eigenspace a A = kernel (A - a *C id_cblinfun)"

lemma kernel_scaleC[simp]: "a0  kernel (a *C A) = kernel A"
  for a :: complex and A :: "(_,_) cblinfun"
  apply transfer
  using complex_vector.scale_eq_0_iff by blast

lemma kernel_0[simp]: "kernel 0 = top"
  by transfer auto

lemma kernel_id[simp]: "kernel id_cblinfun = 0"
  by transfer simp

lemma eigenspace_scaleC[simp]:
  assumes a1: "a  0"
  shows "eigenspace b (a *C A) = eigenspace (b/a) A"
proof -
  have "b *C (id_cblinfun::('a, _) cblinfun) = a *C (b / a) *C id_cblinfun"
    using a1
    by (metis ceq_vector_fraction_iff)
  hence "kernel (a *C A - b *C id_cblinfun) = kernel (A - (b / a) *C id_cblinfun)"
    using a1 by (metis (no_types) complex_vector.scale_right_diff_distrib kernel_scaleC)
  thus ?thesis
    unfolding eigenspace_def
    by blast
qed

lemma eigenspace_memberD:
  assumes "x  space_as_set (eigenspace e A)"
  shows "A *V x = e *C x"
  using assms unfolding eigenspace_def by transfer auto

lemma kernel_memberD:
  assumes "x  space_as_set (kernel A)"
  shows "A *V x = 0"
  using assms by transfer auto

lemma eigenspace_memberI:
  assumes "A *V x = e *C x"
  shows "x  space_as_set (eigenspace e A)"
  using assms unfolding eigenspace_def by transfer auto

lemma kernel_memberI:
  assumes "A *V x = 0"
  shows "x  space_as_set (kernel A)"
  using assms by transfer auto

lemma kernel_Proj[simp]: kernel (Proj S) = - S
  apply transfer
  apply auto
  apply (metis diff_0_right is_projection_on_iff_orthog projection_is_projection_on')
  by (simp add: complex_vector.subspace_0 projection_eqI)

lemma orthogonal_projectors_orthogonal_spaces:
  ― ‹Logically belongs in section "Projectors".›
  fixes S T :: 'a::chilbert_space ccsubspace
  shows orthogonal_spaces S T  Proj S oCL Proj T = 0
proof (intro ballI iffI)
  assume Proj S oCL Proj T = 0 
  then have is_orthogonal x y if x  space_as_set S y  space_as_set T for x y
    by (metis (no_types, opaque_lifting) Proj_fixes_image adj_Proj cblinfun.zero_left cblinfun_apply_cblinfun_compose cinner_adj_right cinner_zero_right that(1) that(2))
  then show orthogonal_spaces S T
    by (simp add: orthogonal_spaces_def)
next 
  assume orthogonal_spaces S T
  then have S  - T
    by (simp add: orthogonal_spaces_leq_compl)
  then show Proj S oCL Proj T = 0
    by (metis (no_types, opaque_lifting) Proj_range adj_Proj adj_cblinfun_compose basic_trans_rules(31) cblinfun.zero_left cblinfun_apply_cblinfun_compose cblinfun_apply_in_image cblinfun_eqI kernel_Proj kernel_memberD less_eq_ccsubspace.rep_eq)
qed


lemma cblinfun_compose_Proj_kernel[simp]: a oCL Proj (kernel a) = 0
  apply (rule cblinfun_eqI)
  by simp (metis Proj_range cblinfun_apply_in_image kernel_memberD)

lemma kernel_compl_adj_range:
  shows kernel a = - (a* *S top)
proof (rule ccsubspace_eqI)
  fix x
  have x  space_as_set (kernel a)  a x = 0
    by transfer simp
  also have a x = 0  (y. is_orthogonal y (a x))
    by (metis cinner_gt_zero_iff cinner_zero_right)
  also have   (y. is_orthogonal (a* *V y) x)
    by (simp add: cinner_adj_left)
  also have   x  space_as_set (- (a* *S top))
    by transfer (metis (mono_tags, opaque_lifting) UNIV_I image_iff is_orthogonal_sym orthogonal_complementI orthogonal_complement_of_closure orthogonal_complement_orthoI')
  finally show x  space_as_set (kernel a)  x  space_as_set (- (a* *S top))
    by -
qed

lemma kernel_apply_self: A *S kernel A = 0
proof transfer
  fix A :: 'b  'a
  assume bounded_clinear A
  then have A 0 = 0
    by (simp add: bounded_clinear_def complex_vector.linear_0)
  then have A ` A -` {0} = {0}
    by fastforce
  then show closure (A ` A -` {0}) = {0}
    by auto
qed

lemma leq_kernel_iff: 
  shows A  kernel B  B *S A = 0
proof (rule iffI)
  assume A  kernel B
  then have B *S A  B *S kernel B
    by (simp add: cblinfun_image_mono)
  also have  = 0
    by (simp add: kernel_apply_self)
  finally show B *S A = 0
    by (simp add: bot.extremum_unique)
next
  assume B *S A = 0
  then show A  kernel B
    apply transfer
    by (metis closure_subset image_subset_iff_subset_vimage)
qed

lemma cblinfun_image_kernel:
  assumes C *S A *S kernel B  kernel B
  assumes A oCL C = id_cblinfun
  shows A *S kernel B = kernel (B oCL C)
proof (rule antisym)
  show A *S kernel B  kernel (B oCL C)
    using assms(1) by (simp add: leq_kernel_iff cblinfun_compose_image)
  show kernel (B oCL C)  A *S kernel B
  proof (insert assms(2), transfer, intro subsetI)
    fix A :: 'a  'b and B :: 'a  'c and C :: 'b  'a and x
    assume x  (B  C) -` {0}
    then have BCx: B (C x) = 0
      by simp
    assume A  C = (λx. x)
    then have x = A (C x)
      apply (simp add: o_def)
      by metis
    then show x  closure (A ` B -` {0})
      using B (C x) = 0 closure_subset by fastforce
  qed
qed

lemma cblinfun_image_kernel_unitary:
  assumes unitary U
  shows U *S kernel B = kernel (B oCL U*)
  apply (rule cblinfun_image_kernel)
  using assms by (auto simp flip: cblinfun_compose_image)

lemma kernel_cblinfun_compose:
  assumes kernel B = 0
  shows kernel A = kernel (B oCL A)
  using assms apply transfer by auto


lemma eigenspace_0[simp]: eigenspace 0 A = kernel A
  by (simp add: eigenspace_def)

lemma kernel_isometry: kernel U = 0 if isometry U
  by (simp add: kernel_compl_adj_range range_adjoint_isometry that)

lemma cblinfun_image_eigenspace_isometry:
  assumes [simp]: isometry A and c  0
  shows A *S eigenspace c B = eigenspace c (sandwich A B)
proof (rule antisym)
  show A *S eigenspace c B  eigenspace c (sandwich A B)
  proof (unfold cblinfun_image_def2, rule ccspan_leqI, rule subsetI)
    fix x assume x  (*V) A ` space_as_set (eigenspace c B)
    then obtain y where x_def: x = A y and y  space_as_set (eigenspace c B)
      by auto
    then have B y = c *C y
      by (simp add: eigenspace_memberD)
    then have sandwich A B x = c *C x
      apply (simp add: sandwich_apply x_def cblinfun_compose_assoc 
          flip: cblinfun_apply_cblinfun_compose)
      by (simp add: cblinfun.scaleC_right)
    then show x  space_as_set (eigenspace c (sandwich A B))
      by (simp add: eigenspace_memberI)
  qed
  show eigenspace c (sandwich A *V B)  A *S eigenspace c B
  proof (rule ccsubspace_leI_unit)
    fix x
    assume x  space_as_set (eigenspace c (sandwich A B))
    then have *: sandwich A B x = c *C x
      by (simp add: eigenspace_memberD)
    then have c *C x  range A
      apply (simp add: sandwich_apply)
      by (metis rangeI)
    then have (inverse c * c) *C x  range A
      apply (simp flip: scaleC_scaleC)
      by (metis (no_types, lifting) cblinfun.scaleC_right rangeE rangeI)
    with c  0 have x  range A
      by simp
    then obtain y where x_def: x = A y
      by auto
    have B *V y = A* *V sandwich A B x
      apply (simp add: sandwich_apply x_def)
      by (metis assms cblinfun_apply_cblinfun_compose id_cblinfun.rep_eq isometryD)
    also have  = c *C y
      apply (simp add: * cblinfun.scaleC_right)
      apply (simp add: x_def)
      by (metis assms(1) cblinfun_apply_cblinfun_compose id_cblinfun_apply isometry_def)
    finally have y  space_as_set (eigenspace c B)
      by (simp add: eigenspace_memberI)
    then show x  space_as_set (A *S eigenspace c B)
      by (simp add: x_def cblinfun_apply_in_image')
  qed
qed

lemma cblinfun_image_eigenspace_unitary:
  assumes [simp]: unitary A
  shows A *S eigenspace c B = eigenspace c (sandwich A B)
  apply (cases c = 0)
   apply (simp add: sandwich_apply cblinfun_image_kernel_unitary kernel_isometry cblinfun_compose_assoc
    flip: kernel_cblinfun_compose)
  by (simp add: cblinfun_image_eigenspace_isometry)

lemma kernel_member_iff: x  space_as_set (kernel A)  A *V x = 0
  using kernel_memberD kernel_memberI by blast

lemma kernel_square[simp]: kernel (A* oCL A) = kernel A
proof (intro ccsubspace_eqI iffI)
  fix x
  assume x  space_as_set (kernel A)
  then show x  space_as_set (kernel (A* oCL A))
    by (simp add: kernel.rep_eq)
next
  fix x
  assume x  space_as_set (kernel (A* oCL A))
  then have A* *V A *V x = 0
    by (simp add: kernel.rep_eq)
  then have (A *V x) C (A *V x) = 0
    by (metis cinner_adj_right cinner_zero_right)
  then have A *V x = 0
    by auto
  then show x  space_as_set (kernel A)
    by (simp add: kernel.rep_eq)
qed

subsection ‹Partial isometries›

definition partial_isometry where
  partial_isometry A  (h  space_as_set (- kernel A). norm (A h) = norm h)

lemma partial_isometryI: 
  assumes h. h  space_as_set (- kernel A)  norm (A h) = norm h
  shows partial_isometry A
  using assms partial_isometry_def by blast

lemma
  fixes A :: 'a :: chilbert_space CL 'b :: complex_normed_vector
  assumes iso: ψ. ψ  space_as_set V  norm (A *V ψ) = norm ψ
  assumes zero: ψ. ψ  space_as_set (- V)  A *V ψ = 0
  shows partial_isometryI': partial_isometry A
    and partial_isometry_initial: kernel A = - V
proof -
  from zero
  have - V  kernel A
    by (simp add: kernel_memberI less_eq_ccsubspace.rep_eq subsetI)
  moreover have kernel A  -V
    by (smt (verit, ccfv_threshold) Proj_ortho_compl Proj_range assms(1) cblinfun.diff_left cblinfun.diff_right cblinfun_apply_in_image cblinfun_id_cblinfun_apply ccsubspace_leI kernel_Proj kernel_memberD kernel_memberI norm_eq_zero ortho_involution subsetI zero)
  ultimately show kerA: kernel A = -V
    by simp

  show partial_isometry A
    apply (rule partial_isometryI)
    by (simp add: kerA iso)
qed

lemma Proj_partial_isometry[simp]: partial_isometry (Proj S)
  apply (rule partial_isometryI)
  by (simp add: Proj_fixes_image)

lemma is_Proj_partial_isometry: is_Proj P  partial_isometry P for P :: _ :: chilbert_space CL _
  by (metis Proj_on_own_range Proj_partial_isometry)

lemma isometry_partial_isometry: isometry P  partial_isometry P
  by (simp add: isometry_preserves_norm partial_isometry_def)

lemma unitary_partial_isometry: unitary P  partial_isometry P
  using isometry_partial_isometry unitary_isometry by blast

lemma norm_partial_isometry:
  fixes A :: 'a :: chilbert_space CL 'b::complex_normed_vector
  assumes partial_isometry A and A  0
  shows norm A = 1 
proof -
  from A  0
  have - (kernel A)  0
    by (metis cblinfun_eqI diff_zero id_cblinfun_apply kernel_id kernel_memberD ortho_involution orthog_proj_exists orthogonal_complement_closed_subspace uminus_ccsubspace.rep_eq zero_cblinfun.rep_eq)
  then obtain h where h  space_as_set (- kernel A) and h  0
    by (metis cblinfun_id_cblinfun_apply ccsubspace_eqI closed_csubspace.subspace complex_vector.subspace_0 kernel_id kernel_memberD kernel_memberI orthogonal_complement_closed_subspace uminus_ccsubspace.rep_eq)
  with partial_isometry A
  have norm (A h) = norm h
    using partial_isometry_def by blast
  then have norm A  1
    by (smt (verit) h  0 mult_cancel_right1 mult_left_le_one_le norm_cblinfun norm_eq_zero norm_ge_zero)

  have norm A  1
  proof (rule norm_cblinfun_bound)
    show 0  (1::real)
      by simp
    fix ψ :: 'a
    define g h where g = Proj (kernel A) ψ and h = Proj (- kernel A) ψ
    have A g = 0
      by (metis Proj_range cblinfun_apply_in_image g_def kernel_memberD)
    moreover from partial_isometry A
    have norm (A h) = norm h
      by (metis Proj_range cblinfun_apply_in_image h_def partial_isometry_def)
    ultimately have norm (A ψ) = norm h
      by (simp add: Proj_ortho_compl cblinfun.diff_left cblinfun.diff_right g_def h_def)
    also have norm h  norm ψ
      by (smt (verit, del_insts) h_def mult_left_le_one_le norm_Proj_leq1 norm_cblinfun norm_ge_zero)
    finally show norm (A *V ψ)  1 * norm ψ
      by simp
  qed

  from norm A  1 and norm A  1
  show norm A = 1
    by auto
qed

lemma partial_isometry_adj_a_o_a:
  assumes partial_isometry a
  shows a* oCL a = Proj (- kernel a)
proof (rule cblinfun_cinner_eqI)
  define P where P = Proj (- kernel a)
  have aP: a oCL P = a
    by (auto intro!: simp: cblinfun_compose_minus_right P_def Proj_ortho_compl)
  have is_Proj_P[simp]: is_Proj P
    by (simp add: P_def)

  fix ψ :: 'a
  have ψ C ((a* oCL a) *V ψ) = a ψ C a ψ
    by (simp add: cinner_adj_right)
  also have  = a (P ψ) C a (P ψ)
    by (metis aP cblinfun_apply_cblinfun_compose)
  also have  = P ψ C P ψ
    by (metis P_def Proj_range assms cblinfun_apply_in_image cdot_square_norm partial_isometry_def)
  also have  = ψ C P ψ
    by (simp flip: cinner_adj_right add: is_proj_selfadj is_Proj_idempotent[THEN simp_a_oCL_b'])
  finally show ψ C ((a* oCL a) *V ψ) = ψ C P ψ
    by -
qed

lemma partial_isometry_square_proj: is_Proj (a* oCL a) if partial_isometry a
  by (simp add: partial_isometry_adj_a_o_a that)

lemma partial_isometry_adj[simp]: partial_isometry (a*) if partial_isometry a
  for a :: 'a::chilbert_space CL 'b::chilbert_space
proof -
  have ran_ker: a *S top = - kernel (a*)
    by (simp add: kernel_compl_adj_range)

  have norm (a* *V h) = norm h if h  range a for h
  proof -
    from that obtain x where h: h = a x
      by auto
    have norm (a* *V h) = norm (a* *V a *V x)
      by (simp add: h)
    also have  = norm (Proj (- kernel a) *V x)
      by (simp add: partial_isometry a partial_isometry_adj_a_o_a simp_a_oCL_b')
    also have  = norm (a *V Proj (- kernel a) *V x)
      by (metis Proj_range partial_isometry a cblinfun_apply_in_image partial_isometry_def)
    also have  = norm (a *V x)
      by (smt (verit, best) Proj_idempotent partial_isometry a adj_Proj cblinfun_apply_cblinfun_compose cinner_adj_right cnorm_eq partial_isometry_adj_a_o_a)
    also have  = norm h
      using h by auto
    finally show ?thesis
      by -
  qed

  then have norm_pres: norm (a* *V h) = norm h if h  closure (range a) for h
    using that apply (rule on_closure_eqI)
      by assumption (intro continuous_intros)+

  show ?thesis
    apply (rule partial_isometryI)
    by (auto simp: cblinfun_image.rep_eq norm_pres simp flip: ran_ker)
qed

subsection ‹Isomorphisms and inverses›

definition iso_cblinfun :: ('a::complex_normed_vector, 'b::complex_normed_vector) cblinfun  bool where
  iso_cblinfun A = ( B. A oCL B = id_cblinfun  B oCL A = id_cblinfun)

definition invertible_cblinfun A  (B. B oCL A = id_cblinfun)

definition cblinfun_inv :: ('a::complex_normed_vector, 'b::complex_normed_vector) cblinfun  ('b,'a) cblinfun where
  cblinfun_inv A = (if invertible_cblinfun A then SOME B. B oCL A = id_cblinfun else 0)

lemma cblinfun_inv_left:
  assumes invertible_cblinfun A
  shows cblinfun_inv A oCL A = id_cblinfun
  apply (simp add: assms cblinfun_inv_def)
  apply (rule someI_ex)
  using assms by (simp add: invertible_cblinfun_def)

lemma inv_cblinfun_invertible:  iso_cblinfun A  invertible_cblinfun A
  by (auto simp: iso_cblinfun_def invertible_cblinfun_def)

lemma cblinfun_inv_right:
  assumes iso_cblinfun A
  shows A oCL cblinfun_inv A = id_cblinfun
proof -
  from assms
  obtain B where AB: A oCL B = id_cblinfun and BA: B oCL A = id_cblinfun
    using iso_cblinfun_def by blast
  from BA have cblinfun_inv A oCL A = id_cblinfun
    by (simp add: assms cblinfun_inv_left inv_cblinfun_invertible)
  with AB BA have cblinfun_inv A = B
    by (metis cblinfun_assoc_left(1) cblinfun_compose_id_right)
  with AB show A oCL cblinfun_inv A = id_cblinfun
    by auto
qed

lemma cblinfun_inv_uniq:
  assumes "A oCL B = id_cblinfun" and "B oCL A = id_cblinfun"
  shows "cblinfun_inv A = B"
  using assms by (metis inv_cblinfun_invertible cblinfun_compose_assoc cblinfun_compose_id_right cblinfun_inv_left iso_cblinfun_def)

lemma iso_cblinfun_unitary: unitary A  iso_cblinfun A
  using iso_cblinfun_def unitary_def by blast

lemma invertible_cblinfun_isometry: isometry A  invertible_cblinfun A
  using invertible_cblinfun_def isometryD by blast

lemma summable_cblinfun_apply_invertible:
  assumes invertible_cblinfun A
  shows (λx. A *V g x) summable_on S  g summable_on S
proof (rule iffI)
  assume g summable_on S
  then show (λx. A *V g x) summable_on S
    by (rule summable_on_cblinfun_apply)
next
  assume (λx. A *V g x) summable_on S
  then have (λx. cblinfun_inv A *V A *V g x) summable_on S
    by (rule summable_on_cblinfun_apply)
  then show g summable_on S
    by (simp add: cblinfun_inv_left assms flip: cblinfun_apply_cblinfun_compose)
qed

lemma infsum_cblinfun_apply_invertible:
  assumes invertible_cblinfun A
  shows (xS. A *V g x) = A *V (xS. g x)
proof (cases g summable_on S)
  case True
  then show ?thesis
    by (rule infsum_cblinfun_apply)
next
  case False
  then have ¬ (λx. A *V g x) summable_on S
  using assms by (simp add: summable_cblinfun_apply_invertible)
  with False show ?thesis 
    by (simp add: infsum_not_exists)
qed

subsection ‹One-dimensional spaces›


instantiation cblinfun :: (one_dim, one_dim) complex_inner begin
text ‹Once we have a theory for the trace, we could instead define the Hilbert-Schmidt inner product
  and relax the classone_dim-sort constraint to (classcfinite_dim,classcomplex_normed_vector) or similar›
definition "cinner_cblinfun (A::'a CL 'b) (B::'a CL 'b)
             = cnj (one_dim_iso (A *V 1)) * one_dim_iso (B *V 1)"
instance
proof intro_classes
  fix A B C :: "'a CL 'b"
    and c c' :: complex
  show "(A C B) = cnj (B C A)"
    unfolding cinner_cblinfun_def by auto
  show "(A + B) C C = (A C C) + (B C C)"
    by (simp add: cinner_cblinfun_def algebra_simps plus_cblinfun.rep_eq)
  show "(c *C A C B) = cnj c * (A C B)"
    by (simp add: cblinfun.scaleC_left cinner_cblinfun_def)
  show "0  (A C A)"
    unfolding cinner_cblinfun_def by auto
  have "bounded_clinear A  A 1 = 0  A = (λ_. 0)"
    for A::"'a  'b"
  proof (rule one_dim_clinear_eqI [where x = 1] , auto)
    show "clinear A"
      if "bounded_clinear A"
        and "A 1 = 0"
      for A :: "'a  'b"
      using that
      by (simp add: bounded_clinear.clinear)
    show "clinear ((λ_. 0)::'a  'b)"
      if "bounded_clinear A"
        and "A 1 = 0"
      for A :: "'a  'b"
      using that
      by (simp add: complex_vector.module_hom_zero)
  qed
  hence "A *V 1 = 0  A = 0"
    by transfer
  hence "one_dim_iso (A *V 1) = 0  A = 0"
    by (metis one_dim_iso_of_zero one_dim_iso_inj)
  thus "((A C A) = 0) = (A = 0)"
    by (auto simp: cinner_cblinfun_def)

  show "norm A = sqrt (cmod (A C A))"
    unfolding cinner_cblinfun_def
    by transfer (simp add: norm_mult abs_complex_def one_dim_onorm' cnj_x_x power2_eq_square bounded_clinear.clinear)
qed
end

instantiation cblinfun :: (one_dim, one_dim) one_dim begin
lift_definition one_cblinfun :: "'a CL 'b" is "one_dim_iso"
  by (rule bounded_clinear_one_dim_iso)
lift_definition times_cblinfun :: "'a CL 'b  'a CL 'b  'a CL 'b"
  is "λf g. f o one_dim_iso o g"
  by (simp add: comp_bounded_clinear)
lift_definition inverse_cblinfun :: "'a CL 'b  'a CL 'b" is
  "λf. ((*) (one_dim_iso (inverse (f 1)))) o one_dim_iso"
  by (auto intro!: comp_bounded_clinear bounded_clinear_mult_right)
definition divide_cblinfun :: "'a CL 'b  'a CL 'b  'a CL 'b" where
  "divide_cblinfun A B = A * inverse B"
definition "canonical_basis_cblinfun = [1 :: 'a CL 'b]"
definition canonical_basis_length_cblinfun (_ :: ('a CL 'b) itself) = (1::nat)
instance
proof intro_classes
  let ?basis = "canonical_basis :: ('a CL 'b) list"
  fix A B C :: "'a CL 'b"
    and c c' :: complex
  show "distinct ?basis"
    unfolding canonical_basis_cblinfun_def by simp
  have "(1::'a CL 'b)  (0::'a CL 'b)"
    by (metis cblinfun.zero_left one_cblinfun.rep_eq one_dim_iso_of_one zero_neq_one)
  thus "cindependent (set ?basis)"
    unfolding canonical_basis_cblinfun_def by simp

  have "A  cspan (set ?basis)" for A
  proof -
    define c :: complex where "c = one_dim_iso (A *V 1)"
    have "A x = one_dim_iso (A 1) *C one_dim_iso x" for x
      by (smt (z3) cblinfun.scaleC_right complex_vector.scale_left_commute one_dim_iso_idem one_dim_scaleC_1)
    hence "A = one_dim_iso (A *V 1) *C 1"
      by transfer metis
    thus "A  cspan (set ?basis)"
      unfolding canonical_basis_cblinfun_def
      by (smt complex_vector.span_base complex_vector.span_scale list.set_intros(1))
  qed
  thus "cspan (set ?basis) = UNIV" by auto

  have "A = (1::'a CL 'b) 
    norm (1::'a CL 'b) = (1::real)"
    by transfer simp
  thus "A  set ?basis  norm A = 1"
    unfolding canonical_basis_cblinfun_def
    by simp

  show "?basis = [1]"
    unfolding canonical_basis_cblinfun_def by simp
  show "c *C 1 * c' *C 1 = (c * c') *C (1::'aCL'b)"
    by transfer auto
  have "(1::'a CL 'b) = (0::'a CL 'b)  False"
    by (metis cblinfun.zero_left one_cblinfun.rep_eq one_dim_iso_of_zero' zero_neq_neg_one)
  thus "is_ortho_set (set ?basis)"
    unfolding is_ortho_set_def canonical_basis_cblinfun_def by auto
  show "A div B = A * inverse B"
    by (simp add: divide_cblinfun_def)
  show "inverse (c *C 1) = (1::'aCL'b) /C c"
    by transfer (simp add: o_def one_dim_inverse)
  show canonical_basis_length TYPE('a CL 'b) = length (canonical_basis :: ('a CL 'b) list)
    by (simp add: canonical_basis_length_cblinfun_def canonical_basis_cblinfun_def)
qed
end

lemma id_cblinfun_eq_1[simp]: id_cblinfun = 1
  by transfer auto

lemma one_dim_cblinfun_compose_is_times[simp]:
  fixes A :: "'a::one_dim CL 'a" and B :: "'a CL 'a"
  shows "A oCL B = A * B"
  by transfer simp

lemma scaleC_one_dim_is_times: r *C x = one_dim_iso r * x
  by simp

lemma one_comp_one_cblinfun[simp]: "1 oCL 1 = 1"
  apply transfer unfolding o_def by simp

lemma one_cblinfun_adj[simp]: "1* = 1"
  by transfer simp

lemma scaleC_1_apply[simp]: (x *C 1) *V y = x *C y
  by (metis cblinfun.scaleC_left cblinfun_id_cblinfun_apply id_cblinfun_eq_1)

lemma cblinfun_apply_1_left[simp]: 1 *V y = y
  by (metis cblinfun_id_cblinfun_apply id_cblinfun_eq_1)

lemma of_complex_cblinfun_apply[simp]: of_complex x *V y = one_dim_iso (x *C y)
  by (metis of_complex_def cblinfun.scaleC_right one_cblinfun.rep_eq scaleC_cblinfun.rep_eq)

lemma cblinfun_compose_1_left[simp]: 1 oCL x = x
  by transfer auto

lemma cblinfun_compose_1_right[simp]: x oCL 1 = x
  by transfer auto

lemma one_dim_iso_id_cblinfun: one_dim_iso id_cblinfun = id_cblinfun
  by simp

lemma one_dim_iso_id_cblinfun_eq_1: one_dim_iso id_cblinfun = 1
  by simp

lemma one_dim_iso_comp_distr[simp]: one_dim_iso (a oCL b) = one_dim_iso a oCL one_dim_iso b
  by (smt (z3) cblinfun_compose_scaleC_left cblinfun_compose_scaleC_right one_cinner_a_scaleC_one one_comp_one_cblinfun one_dim_iso_of_one one_dim_iso_scaleC)

lemma one_dim_iso_comp_distr_times[simp]: one_dim_iso (a oCL b) = one_dim_iso a * one_dim_iso b
  by (smt (verit, del_insts) mult.left_neutral mult_scaleC_left one_cinner_a_scaleC_one one_comp_one_cblinfun one_dim_iso_of_one one_dim_iso_scaleC cblinfun_compose_scaleC_right cblinfun_compose_scaleC_left)

lemma one_dim_iso_adjoint[simp]: one_dim_iso (A*) = (one_dim_iso A)*
  by (smt (z3) one_cblinfun_adj one_cinner_a_scaleC_one one_dim_iso_of_one one_dim_iso_scaleC scaleC_adj)

lemma one_dim_iso_adjoint_complex[simp]: one_dim_iso (A*) = cnj (one_dim_iso A)
  by (metis (mono_tags, lifting) one_cblinfun_adj one_dim_iso_idem one_dim_scaleC_1 scaleC_adj)

lemma one_dim_cblinfun_compose_commute: a oCL b = b oCL a for a b :: ('a::one_dim,'a) cblinfun
  by (simp add: one_dim_iso_inj)

lemma one_cblinfun_apply_one[simp]: 1 *V 1 = 1
  by (simp add: one_cblinfun.rep_eq)

lemma one_dim_cblinfun_apply_is_times:
  fixes A :: "'a::one_dim CL 'b::one_dim" and b :: "'a"
  shows "A *V b = one_dim_iso A * one_dim_iso b"
  apply (subst one_dim_scaleC_1[of A, symmetric])
  apply (subst one_dim_scaleC_1[of b, symmetric])
  apply (simp only: cblinfun.scaleC_left cblinfun.scaleC_right)
  by simp

lemma is_onb_one_dim[simp]: norm x = 1  is_onb {x} for x :: _ :: one_dim
  by (auto simp: is_onb_def intro!: ccspan_one_dim)

lemma one_dim_iso_cblinfun_comp: one_dim_iso (a oCL b) = of_complex (cinner (a* *V 1) (b *V 1))
  for a :: 'a::chilbert_space CL 'b::one_dim and b :: 'c::one_dim CL 'a
  by (simp add: cinner_adj_left cinner_cblinfun_def one_dim_iso_def)

lemma one_dim_iso_cblinfun_apply[simp]: one_dim_iso ψ *V φ = one_dim_iso (one_dim_iso ψ *C φ)
  by (smt (verit) cblinfun.scaleC_left one_cblinfun.rep_eq one_dim_iso_of_one one_dim_iso_scaleC one_dim_scaleC_1)

subsection ‹Loewner order›

lift_definition heterogenous_cblinfun_id :: 'a::complex_normed_vector CL 'b::complex_normed_vector
  is if bounded_clinear (heterogenous_identity :: 'a::complex_normed_vector  'b::complex_normed_vector) then heterogenous_identity else (λ_. 0)
  by auto

lemma heterogenous_cblinfun_id_def'[simp]: "heterogenous_cblinfun_id = id_cblinfun"
  by transfer auto

definition "heterogenous_same_type_cblinfun (x::'a::chilbert_space itself) (y::'b::chilbert_space itself) 
  unitary (heterogenous_cblinfun_id :: 'a CL 'b)  unitary (heterogenous_cblinfun_id :: 'b CL 'a)"

lemma heterogenous_same_type_cblinfun[simp]: heterogenous_same_type_cblinfun (x::'a::chilbert_space itself) (y::'a::chilbert_space itself)
  unfolding heterogenous_same_type_cblinfun_def by auto

instantiation cblinfun :: (chilbert_space, chilbert_space) ord begin
definition less_eq_cblinfun_def_heterogenous: A  B 
  (if heterogenous_same_type_cblinfun TYPE('a) TYPE('b) then
    ψ::'b. cinner ψ ((B-A) *V heterogenous_cblinfun_id *V ψ)  0 else (A=B))
definition (A :: 'a CL 'b) < B  A  B  ¬ B  A
instance..
end

lemma less_eq_cblinfun_def: A  B 
    (ψ. cinner ψ (A *V ψ)  cinner ψ (B *V ψ))
  unfolding less_eq_cblinfun_def_heterogenous
  by (auto simp del: less_eq_complex_def simp: cblinfun.diff_left cinner_diff_right)

instantiation cblinfun :: (chilbert_space, chilbert_space) ordered_complex_vector begin
instance
proof intro_classes
  fix x y z :: 'a CL 'b
  fix a b :: complex

  define pos where pos X  (ψ. cinner ψ (X *V ψ)  0) for X :: 'b CL 'b
  consider (unitary) heterogenous_same_type_cblinfun TYPE('a) TYPE('b)
      A B :: 'a CL 'b. A  B = pos ((B-A) oCL (heterogenous_cblinfun_id :: 'bCL'a))
    | (trivial) A B :: 'a CL 'b. A  B  A = B
    by atomize_elim (auto simp: pos_def less_eq_cblinfun_def_heterogenous)
  note cases = this

  have [simp]: pos 0
    unfolding pos_def by auto

  have pos_nondeg: X = 0 if pos X and pos (-X) for X
    apply (rule cblinfun_cinner_eqI, simp)
    using that by (metis (no_types, lifting) cblinfun.minus_left cinner_minus_right dual_order.antisym equation_minus_iff neg_le_0_iff_le pos_def)

  have pos_add: pos (X+Y) if pos X and pos Y for X Y
    by (smt (z3) pos_def cblinfun.diff_left cinner_minus_right cinner_simps(3) diff_ge_0_iff_ge diff_minus_eq_add neg_le_0_iff_le order_trans that(1) that(2) uminus_cblinfun.rep_eq)

  have pos_scaleC: pos (a *C X) if a0 and pos X for X a
    using that unfolding pos_def by (auto simp: cblinfun.scaleC_left)

  let ?id = heterogenous_cblinfun_id :: 'b CL 'a

  show x  x
    apply (cases rule:cases) by auto
  show (x < y)  (x  y  ¬ y  x)
    unfolding less_cblinfun_def by simp
  show x  z if x  y and y  z
  proof (cases rule:cases)
    case unitary
    define a b :: 'b CL 'b where a = (y-x) oCL heterogenous_cblinfun_id
      and b = (z-y) oCL heterogenous_cblinfun_id
    with unitary that have pos a and pos b
      by auto
    then have pos (a + b)
      by (rule pos_add)
    moreover have a + b = (z - x) oCL heterogenous_cblinfun_id
      unfolding a_def b_def
      by (metis (no_types, lifting) bounded_cbilinear.add_left bounded_cbilinear_cblinfun_compose diff_add_cancel ordered_field_class.sign_simps(2) ordered_field_class.sign_simps(8))
    ultimately show ?thesis
      using unitary by auto
  next
    case trivial
    with that show ?thesis by auto
  qed
  show x = y if x  y and y  x
  proof (cases rule:cases)
    case unitary
    then have unitary ?id
      by (auto simp: heterogenous_same_type_cblinfun_def)
    define a b :: 'b CL 'b where a = (y-x) oCL ?id
      and b = (x-y) oCL ?id
    with unitary that have pos a and pos b
      by auto
    then have a = 0
      apply (rule_tac pos_nondeg)
       apply (auto simp: a_def b_def)
      by (smt (verit, best) add.commute bounded_cbilinear.add_left bounded_cbilinear_cblinfun_compose cblinfun_compose_zero_left diff_0 diff_add_cancel group_cancel.rule0 group_cancel.sub1)
    then show ?thesis
      unfolding a_def using unitary ?id
      by (metis cblinfun_compose_assoc cblinfun_compose_id_right cblinfun_compose_zero_left eq_iff_diff_eq_0 unitaryD2)
  next
    case trivial
    with that show ?thesis by simp
  qed
  show x + y  x + z if y  z
  proof (cases rule:cases)
    case unitary
    with that show ?thesis
      by auto
  next
    case trivial
    with that show ?thesis
      by auto
  qed

  show a *C x  a *C y if x  y and 0  a
  proof (cases rule:cases)
    case unitary
    with that pos_scaleC show ?thesis
      by (metis cblinfun_compose_scaleC_left complex_vector.scale_right_diff_distrib)
  next
    case trivial
    with that show ?thesis
      by auto
  qed

  show a *C x  b *C x if a  b and 0  x
  proof (cases rule:cases)
    case unitary
    with that show ?thesis
      by (auto intro!: pos_scaleC simp flip: scaleC_diff_left)
  next
    case trivial
    with that show ?thesis
      by auto
  qed
qed
end



lemma positive_id_cblinfun[simp]: "id_cblinfun  0"
  unfolding less_eq_cblinfun_def using cinner_ge_zero by auto

lemma positive_hermitianI: A* = A if A  0
  apply (rule cinner_real_hermiteanI)
  using that by (auto simp: complex_is_real_iff_compare0 less_eq_cblinfun_def)

lemma cblinfun_leI:
  assumes x. norm x = 1  x C (A *V x)  x C (B *V x)
  shows A  B
proof (unfold less_eq_cblinfun_def, intro allI, case_tac ψ = 0)
  fix ψ :: 'a assume ψ = 0
  then show ψ C (A *V ψ)  ψ C (B *V ψ)
    by simp
next
  fix ψ :: 'a assume ψ  0
  define φ where φ = ψ /R norm ψ
  have φ C (A *V φ)  φ C (B *V φ)
    apply (rule assms)
    unfolding φ_def
    by (simp add: ψ  0)
  with ψ  0 show ψ C (A *V ψ)  ψ C (B *V ψ)
    unfolding φ_def
    by (smt (verit) cinner_adj_left cinner_scaleR_left cinner_simps(6) complex_of_real_nn_iff mult_cancel_right1 mult_left_mono norm_eq_zero norm_ge_zero of_real_1 right_inverse scaleR_scaleC scaleR_scaleR)
qed

lemma positive_cblinfunI: A  0 if x. norm x = 1  cinner x (A *V x)  0
  apply (rule cblinfun_leI)
  using that by simp

lemma less_eq_scaled_id_norm: 
  assumes norm A  c and selfadjoint A
  shows A  c *R id_cblinfun
proof -
  have x C (A *V x)  complex_of_real c if norm x = 1 for x
  proof -
    have norm (x C (A *V x))  norm (A *V x)
      by (metis complex_inner_class.Cauchy_Schwarz_ineq2 mult_cancel_right1 that)
    also have   norm A
      by (metis more_arith_simps(6) norm_cblinfun that)
    also have   c
      by (rule assms)
    finally have norm (x C (A *V x))  c
      by -
    moreover have x C (A *V x)  
      by (metis assms(2) cinner_hermitian_real)
    ultimately show ?thesis
      by (smt (verit) Re_complex_of_real Reals_cases complex_of_real_nn_iff less_eq_complex_def norm_of_real reals_zero_comparable)
  qed
  then show ?thesis
    by (smt (verit) cblinfun.scaleC_left cblinfun_id_cblinfun_apply cblinfun_leI cinner_scaleC_right cnorm_eq_1 mult_cancel_left2 scaleR_scaleC)
qed

(* Note: this does not require B to be a square operator *)
lemma positive_cblinfun_squareI: A = B* oCL B  A  0
  apply (rule positive_cblinfunI)
  by (metis cblinfun_apply_cblinfun_compose cinner_adj_right cinner_ge_zero)

lemma one_dim_loewner_order: A  B  one_dim_iso A  (one_dim_iso B :: complex) for A B :: 'a CL 'a::{chilbert_space, one_dim}
proof -
  have A: A = one_dim_iso A *C id_cblinfun
    by simp
  have B: B = one_dim_iso B *C id_cblinfun
    by simp
  have A  B  (ψ. cinner ψ (A ψ)  cinner ψ (B ψ))
    by (simp add: less_eq_cblinfun_def)
  also have   (ψ::'a. one_dim_iso B * (ψ C ψ)  one_dim_iso A * (ψ C ψ))
    apply (subst A, subst B)
    by (metis (no_types, opaque_lifting) cinner_scaleC_right id_cblinfun_apply scaleC_cblinfun.rep_eq)
  also have   one_dim_iso A  (one_dim_iso B :: complex)
    by (auto intro!: mult_right_mono elim!: allE[where x=1])
  finally show ?thesis
    by -
qed

lemma one_dim_positive: A  0  one_dim_iso A  (0::complex) for A :: 'a CL 'a::{chilbert_space, one_dim}
  using one_dim_loewner_order[where B=0] by auto

lemma op_square_nondegenerate: a = 0 if a* oCL a = 0
proof (rule cblinfun_eq_0_on_UNIV_span[where basis=UNIV]; simp)
  fix s
  from that have s C ((a* oCL a) *V s) = 0
    by simp
  then have (a *V s) C (a *V s) = 0
    by (simp add: cinner_adj_right)
  then show a *V s = 0
    by simp
qed

lemma comparable_hermitean:
  assumes a  b
  assumes selfadjoint a
  shows selfadjoint b
  by (smt (verit, best) assms(1) assms(2) cinner_hermitian_real cinner_real_hermiteanI comparable complex_is_real_iff_compare0 less_eq_cblinfun_def selfadjoint_def)

lemma comparable_hermitean':
  assumes a  b
  assumes selfadjoint b
  shows selfadjoint a
  by (smt (verit, best) assms(1) assms(2) cinner_hermitian_real cinner_real_hermiteanI comparable complex_is_real_iff_compare0 less_eq_cblinfun_def selfadjoint_def)

lemma Proj_mono: Proj S  Proj T  S  T
proof (rule iffI)
  assume S  T
  define D where D = Proj T - Proj S
  from S  T have TS_S[simp]: Proj T oCL Proj S = Proj S
    by (smt (verit, ccfv_threshold) Proj_idempotent Proj_range cblinfun_apply_cblinfun_compose cblinfun_apply_in_image cblinfun_eqI cblinfun_fixes_range less_eq_ccsubspace.rep_eq subset_iff)
  then have ST_S[simp]: Proj S oCL Proj T = Proj S
    by (metis adj_Proj adj_cblinfun_compose)
  have D* oCL D = D
    by (simp add: D_def cblinfun_compose_minus_left cblinfun_compose_minus_right adj_minus adj_Proj)
  then have D  0
    by (metis positive_cblinfun_squareI)
  then show Proj S  Proj T
    by (simp add: D_def)
next
  assume PS_PT: Proj S  Proj T
  show S  T
  proof (rule ccsubspace_leI_unit)
    fix ψ assume ψ  space_as_set S and [simp]: norm ψ = 1
    then have 1 = norm (Proj S *V ψ)
      by (simp add: Proj_fixes_image)
    also from PS_PT have   norm (Proj T *V ψ)
      by (metis (no_types, lifting) Proj_idempotent adj_Proj cblinfun_apply_cblinfun_compose cinner_adj_left cnorm_le less_eq_cblinfun_def)
    also have   1
      by (metis Proj_is_Proj norm ψ = 1 is_Proj_reduces_norm)
    ultimately have norm (Proj T *V ψ) = 1
      by auto
    then show ψ  space_as_set T
      by (simp add: norm_Proj_apply_1)
  qed
qed

subsection ‹Embedding vectors to operators›

lift_definition vector_to_cblinfun :: 'a::complex_normed_vector  'b::one_dim CL 'a is
  λψ φ. one_dim_iso φ *C ψ
  by (simp add: bounded_clinear_scaleC_const)

lemma vector_to_cblinfun_cblinfun_compose[simp]:
  "A  oCL (vector_to_cblinfun ψ) = vector_to_cblinfun (A *V ψ)"
  apply transfer
  unfolding comp_def bounded_clinear_def clinear_def Vector_Spaces.linear_def
    module_hom_def module_hom_axioms_def
  by simp

lemma vector_to_cblinfun_add: vector_to_cblinfun (x + y) = vector_to_cblinfun x + vector_to_cblinfun y
  by transfer (simp add: scaleC_add_right)

lemma norm_vector_to_cblinfun[simp]: "norm (vector_to_cblinfun x) = norm x"
proof transfer
  have "bounded_clinear (one_dim_iso::'a  complex)"
    by simp
  moreover have "onorm (one_dim_iso::'a  complex) * norm x = norm x"
    for x :: 'b
    by simp
  ultimately show "onorm (λφ. one_dim_iso (φ::'a) *C x) = norm x"
    for x :: 'b
    by (subst onorm_scaleC_left)
qed

lemma bounded_clinear_vector_to_cblinfun[bounded_clinear]: "bounded_clinear vector_to_cblinfun"
  apply (rule bounded_clinearI[where K=1])
    apply (transfer, simp add: scaleC_add_right)
   apply (transfer, simp add: mult.commute)
  by simp

lemma vector_to_cblinfun_scaleC[simp]:
  "vector_to_cblinfun (a *C ψ) = a *C vector_to_cblinfun ψ" for a::complex
  by (intro clinear.scaleC bounded_clinear.clinear bounded_clinear_vector_to_cblinfun)

lemma vector_to_cblinfun_apply_one_dim[simp]:
  shows "vector_to_cblinfun φ *V γ = one_dim_iso γ *C φ"
  by transfer (rule refl)

lemma vector_to_cblinfun_one_dim_iso[simp]: vector_to_cblinfun = one_dim_iso
  by (auto intro!: ext cblinfun_eqI)

lemma vector_to_cblinfun_adj_apply[simp]:
  shows "vector_to_cblinfun ψ* *V φ = of_complex (cinner ψ φ)"
  by (simp add: cinner_adj_right one_dim_iso_def one_dim_iso_inj)

lemma vector_to_cblinfun_comp_one[simp]:
  "(vector_to_cblinfun s :: 'a::one_dim CL _) oCL 1
     = (vector_to_cblinfun s :: 'b::one_dim CL _)"
  apply (transfer fixing: s)
  by fastforce

lemma vector_to_cblinfun_0[simp]: "vector_to_cblinfun 0 = 0"
  by (metis cblinfun.zero_left cblinfun_compose_zero_left vector_to_cblinfun_cblinfun_compose)

lemma image_vector_to_cblinfun[simp]: "vector_to_cblinfun x *S  = ccspan {x}"
  ― ‹Not that the general case termvector_to_cblinfun x *S S can be handled by using
      that S = ⊤› or S = ⊥› by @{thm [source] one_dim_ccsubspace_all_or_nothing}
proof transfer
  show "closure (range (λφ::'b. one_dim_iso φ *C x)) = closure (cspan {x})"
    for x :: 'a
  proof (rule arg_cong [where f = closure])
    have "k *C x  range (λφ. one_dim_iso φ *C x)" for k
      by (smt (z3) id_apply one_dim_iso_id one_dim_iso_idem range_eqI)
    thus "range (λφ. one_dim_iso (φ::'b) *C x) = cspan {x}"
      unfolding complex_vector.span_singleton
      by auto
  qed
qed

lemma vector_to_cblinfun_adj_comp_vector_to_cblinfun[simp]:
  shows "vector_to_cblinfun ψ* oCL vector_to_cblinfun φ = cinner ψ φ *C id_cblinfun"
proof -
  have "one_dim_iso γ *C one_dim_iso (of_complex (ψ C φ)) =
    (ψ C φ) *C one_dim_iso γ"
    for γ :: "'c::one_dim"
    by (metis complex_vector.scale_left_commute of_complex_def one_dim_iso_of_one one_dim_iso_scaleC one_dim_scaleC_1)
  hence "one_dim_iso ((vector_to_cblinfun ψ* oCL vector_to_cblinfun φ) *V γ)
      = one_dim_iso ((cinner ψ φ *C id_cblinfun) *V γ)"
    for γ :: "'c::one_dim"
    by simp
  hence "((vector_to_cblinfun ψ* oCL vector_to_cblinfun φ) *V γ) = ((cinner ψ φ *C id_cblinfun) *V γ)"
    for γ :: "'c::one_dim"
    by (rule one_dim_iso_inj)
  thus ?thesis
    using cblinfun_eqI[where x = "vector_to_cblinfun ψ* oCL vector_to_cblinfun φ"
        and y = "(ψ C φ) *C id_cblinfun"]
    by auto
qed

lemma isometry_vector_to_cblinfun[simp]:
  assumes "norm x = 1"
  shows "isometry (vector_to_cblinfun x)"
  using assms cnorm_eq_1 isometry_def by force

lemma image_vector_to_cblinfun_adj: 
  assumes ψ  space_as_set (- S)
  shows (vector_to_cblinfun ψ)* *S S = 
proof -
  from assms obtain φ where φ  space_as_set S and ¬ is_orthogonal ψ φ
    by (metis orthogonal_complementI uminus_ccsubspace.rep_eq)
  have ((vector_to_cblinfun ψ)* *S S :: 'b ccsubspace)  (vector_to_cblinfun ψ)* *S ccspan {φ} (is _  )
    by (simp add: φ  space_as_set S cblinfun_image_mono ccspan_leqI)
  also have  = ccspan {(vector_to_cblinfun ψ)* *V φ}
    by (auto simp: cblinfun_image_ccspan)
  also have  = ccspan {of_complex (ψ C φ)}
    by auto
  also have  > 
    by (simp add: ψ C φ  0 flip: bot.not_eq_extremum )
  finally(dual_order.strict_trans1) show ?thesis
    using one_dim_ccsubspace_all_or_nothing bot.not_eq_extremum by auto
qed


lemma image_vector_to_cblinfun_adj': 
  assumes ψ  0
  shows (vector_to_cblinfun ψ)* *S  = 
  apply (rule image_vector_to_cblinfun_adj)
  using assms by simp

subsection ‹Rank-1 operators / butterflies›

definition rank1 where rank1 A  (ψ. A *S  = ccspan {ψ})
(* TODO update description in paper *)
― ‹This is not the usual definition of a rank-1 operator.
The usual definition is an operator with 1-dim image.
Here we define it as an operator with 0- or 1-dim image.
This makes the definition simpler to use.
The normal definition of rank-1 operators then corresponds to the non-zero termrank1 operators.›

definition "butterfly (s::'a::complex_normed_vector) (t::'b::chilbert_space)
   = vector_to_cblinfun s oCL (vector_to_cblinfun t :: complex CL _)*"

abbreviation "selfbutter s  butterfly s s"

lemma butterfly_add_left: butterfly (a + a') b = butterfly a b + butterfly a' b
  by (simp add: butterfly_def vector_to_cblinfun_add cbilinear_add_left bounded_cbilinear.add_left bounded_cbilinear_cblinfun_compose)

lemma butterfly_add_right: butterfly a (b + b') = butterfly a b + butterfly a b'
  by (simp add: butterfly_def adj_plus vector_to_cblinfun_add cblinfun_compose_add_right)

lemma butterfly_def_one_dim: "butterfly s t = (vector_to_cblinfun s :: 'c::one_dim CL _)
                                          oCL (vector_to_cblinfun t :: 'c CL _)*"
  (is "_ = ?rhs") for s :: "'a::complex_normed_vector" and t :: "'b::chilbert_space"
proof -
  let ?isoAC = "1 :: 'c CL complex"
  let ?isoCA = "1 :: complex CL 'c"
  let ?vector = "vector_to_cblinfun :: _  ('c CL _)"

  have "butterfly s t =
    (?vector s oCL ?isoCA) oCL (?vector t oCL ?isoCA)*"
    unfolding butterfly_def vector_to_cblinfun_comp_one by simp
  also have " = ?vector s oCL (?isoCA oCL ?isoCA*) oCL (?vector t)*"
    by (metis (no_types, lifting) cblinfun_compose_assoc adj_cblinfun_compose)
  also have " = ?rhs"
    by simp
  finally show ?thesis
    by simp
qed

lemma butterfly_comp_cblinfun: "butterfly ψ φ oCL a = butterfly ψ (a* *V φ)"
  unfolding butterfly_def
  by (simp add: cblinfun_compose_assoc flip: vector_to_cblinfun_cblinfun_compose)

lemma cblinfun_comp_butterfly: "a oCL butterfly ψ φ = butterfly (a *V ψ) φ"
  unfolding butterfly_def
  by (simp add: cblinfun_compose_assoc flip: vector_to_cblinfun_cblinfun_compose)

lemma butterfly_apply[simp]: "butterfly ψ ψ' *V φ = (ψ' C φ) *C ψ"
  by (simp add: butterfly_def scaleC_cblinfun.rep_eq)

lemma butterfly_scaleC_left[simp]: "butterfly (c *C ψ) φ = c *C butterfly ψ φ"
  unfolding butterfly_def vector_to_cblinfun_scaleC scaleC_adj
  by (simp add: cnj_x_x)

lemma butterfly_scaleC_right[simp]: "butterfly ψ (c *C φ) = cnj c *C butterfly ψ φ"
  unfolding butterfly_def vector_to_cblinfun_scaleC scaleC_adj
  by (simp add: cnj_x_x)

lemma butterfly_scaleR_left[simp]: "butterfly (r *R ψ) φ = r *C butterfly ψ φ"
  by (simp add: scaleR_scaleC)

lemma butterfly_scaleR_right[simp]: "butterfly ψ (r *R φ) = r *C butterfly ψ φ"
  by (simp add: butterfly_scaleC_right scaleR_scaleC)

lemma butterfly_adjoint[simp]: "(butterfly ψ φ)* = butterfly φ ψ"
  unfolding butterfly_def by auto

lemma butterfly_comp_butterfly[simp]: "butterfly ψ1 ψ2 oCL butterfly ψ3 ψ4 = (ψ2 C ψ3) *C butterfly ψ1 ψ4"
  by (simp add: butterfly_comp_cblinfun)

lemma butterfly_0_left[simp]: "butterfly 0 a = 0"
  by (simp add: butterfly_def)

lemma butterfly_0_right[simp]: "butterfly a 0 = 0"
  by (simp add: butterfly_def)

lemma butterfly_is_rank1:
  assumes φ  0
  shows butterfly ψ φ *S  = ccspan {ψ}
  using assms by (simp add: butterfly_def cblinfun_compose_image image_vector_to_cblinfun_adj')


lemma rank1_is_butterfly:
  ― ‹The restriction termψ::_::chilbert_space is necessary.
    Consider, e.g., the space of all finite sequences (with sum-norm), and termA' f = (x. f x).
    Then termA' is not a butterfly.›
  assumes A *S  = ccspan {ψ::_::chilbert_space}
  shows φ. A = butterfly ψ φ
proof (rule exI[of _ A* *V (ψ /R (norm ψ)2)], rule cblinfun_eqI)
  fix γ :: 'b
  from assms have A *V γ  space_as_set (ccspan {ψ})
    by (simp flip: assms)
  then obtain c where c: A *V γ = c *C ψ
    apply atomize_elim
    apply (auto simp: ccspan.rep_eq)
    by (metis complex_vector.span_breakdown_eq complex_vector.span_empty eq_iff_diff_eq_0 singletonD)
  have A *V γ = butterfly ψ (ψ /R (norm ψ)2) *V (A *V γ)
    apply (auto simp: c simp flip: scaleC_scaleC)
    by (metis cinner_eq_zero_iff divideC_field_simps(1) power2_norm_eq_cinner scaleC_left_commute scaleC_zero_right)
  also have  = (butterfly ψ (ψ /R (norm ψ)2) oCL A) *V γ
    by simp
  also have  = butterfly ψ (A* *V (ψ /R (norm ψ)2)) *V γ
    by (simp add: cinner_adj_left)
  finally show A *V γ = 
    by -
qed

lemma rank1_0[simp]: rank1 0
  by (metis ccspan_0 kernel_0 kernel_apply_self rank1_def)

lemma rank1_iff_butterfly: rank1 A  (ψ φ. A = butterfly ψ φ)
  for A :: _::complex_inner CL _::chilbert_space
proof (rule iffI)
  assume rank1 A
  then obtain ψ where A *S  = ccspan {ψ}
    using rank1_def by auto
  then have φ. A = butterfly ψ φ
    by (rule rank1_is_butterfly)
  then show ψ φ. A = butterfly ψ φ
    by auto
next
  assume asm: ψ φ. A = butterfly ψ φ
  show rank1 A
  proof (cases A = 0)
    case True
    then show ?thesis
      by simp
  next
    case False
    from asm obtain ψ φ where A: A = butterfly ψ φ
      by auto
    with False have ψ  0 and φ  0
      by auto
    then have butterfly ψ φ *S  = ccspan {ψ}
      by (rule_tac butterfly_is_rank1)
    with A ψ  0 show rank1 A
      by (auto intro!: exI[of _ ψ] simp: rank1_def)
  qed
qed

lemma norm_butterfly: "norm (butterfly ψ φ) = norm ψ * norm φ"
proof (cases "φ=0")
  case True
  then show ?thesis by simp
next
  case False
  show ?thesis
    unfolding norm_cblinfun.rep_eq
  proof (rule onormI[OF _ False])
    fix x

    have "cmod (φ C x) * norm ψ  norm ψ * norm φ * norm x"
      by (metis ab_semigroup_mult_class.mult_ac(1) complex_inner_class.Cauchy_Schwarz_ineq2 mult.commute mult_left_mono norm_ge_zero)
    thus "norm (butterfly ψ φ *V x)  norm ψ * norm φ * norm x"
      by (simp add: power2_eq_square)

    show "norm (butterfly ψ φ *V φ) = norm ψ * norm φ * norm φ"
      by (smt (z3) ab_semigroup_mult_class.mult_ac(1) butterfly_apply mult.commute norm_eq_sqrt_cinner norm_ge_zero norm_scaleC power2_eq_square real_sqrt_abs real_sqrt_eq_iff)
  qed
qed

lemma bounded_sesquilinear_butterfly[bounded_sesquilinear]: bounded_sesquilinear (λ(b::'b::chilbert_space) (a::'a::chilbert_space). butterfly a b)
proof standard
  fix a a' :: 'a and b b' :: 'b and r :: complex
  show butterfly (a + a') b = butterfly a b + butterfly a' b
    by (rule butterfly_add_left)
  show butterfly a (b + b') = butterfly a b + butterfly a b'
    by (rule butterfly_add_right)
  show butterfly (r *C a) b = r *C butterfly a b
    by simp
  show butterfly a (r *C b) = cnj r *C butterfly a b
    by simp
  show K. b a. norm (butterfly a b)  norm b * norm a * K
    apply (rule exI[of _ 1])
    by (simp add: norm_butterfly)
qed

lemma inj_selfbutter_upto_phase:
  assumes "selfbutter x = selfbutter y"
  shows "c. cmod c = 1  x = c *C y"
proof (cases "x = 0")
  case True
  from assms have "y = 0"
    using norm_butterfly
    by (metis True butterfly_0_left divisors_zero norm_eq_zero)
  with True show ?thesis
    using norm_one by fastforce
next
  case False
  define c where "c = (y C x) / (x C x)"
  have "(x C x) *C x = selfbutter x *V x"
    by (simp add: butterfly_apply)
  also have " = selfbutter y *V x"
    using assms by simp
  also have " = (y C x) *C y"
    by (simp add: butterfly_apply)
  finally have xcy: "x = c *C y"
    by (simp add: c_def ceq_vector_fraction_iff)
  have "cmod c * norm x = cmod c * norm y"
    using assms norm_butterfly
    by (smt (verit, ccfv_SIG) (x C x) *C x = selfbutter x *V x selfbutter y *V x = (y C x) *C y cinner_scaleC_right complex_vector.scale_left_commute complex_vector.scale_right_imp_eq mult_cancel_left norm_eq_sqrt_cinner norm_eq_zero scaleC_scaleC xcy)
  also have "cmod c * norm y = norm (c *C y)"
    by simp
  also have " = norm x"
    unfolding xcy[symmetric] by simp
  finally have c: "cmod c = 1"
    by (simp add: False)
  from c xcy show ?thesis
    by auto
qed

lemma butterfly_eq_proj:
  assumes "norm x = 1"
  shows "selfbutter x = proj x"
proof -
  define B and φ :: "complex CL 'a"
    where "B = selfbutter x" and "φ = vector_to_cblinfun x"
  then have B: "B = φ oCL φ*"
    unfolding butterfly_def by simp
  have φadjφ: "φ* oCL φ = id_cblinfun"
    using φ_def assms isometry_def isometry_vector_to_cblinfun by blast
  have "B oCL B = φ oCL (φ* oCL φ) oCL φ*"
    by (simp add: B cblinfun_assoc_left(1))
  also have " = B"
    unfolding φadjφ by (simp add: B)
  finally have idem: "B oCL B = B".
  have herm: "B = B*"
    unfolding B by simp
  from idem herm have BProj: "B = Proj (B *S top)"
    by (rule Proj_on_own_range'[symmetric])
  have "B *S top = ccspan {x}"
    by (simp add: B φ_def assms cblinfun_compose_image range_adjoint_isometry)
  with BProj show "B = proj x"
    by simp
qed

lemma butterfly_sgn_eq_proj:
  shows "selfbutter (sgn x) = proj x"
proof (cases x = 0)
  case True
  then show ?thesis
    by simp
next
  case False
  then have selfbutter (sgn x) = proj (sgn x)
    by (simp add: butterfly_eq_proj norm_sgn)
  also have ccspan {sgn x} = ccspan {x}
    by (metis ccspan_singleton_scaleC scaleC_eq_0_iff scaleR_scaleC sgn_div_norm sgn_zero_iff)
  finally show ?thesis
    by -
qed

lemma butterfly_is_Proj:
  norm x = 1  is_Proj (selfbutter x)
  by (subst butterfly_eq_proj, simp_all)

lemma cspan_butterfly_UNIV:
  assumes cspan basisA = UNIV
  assumes cspan basisB = UNIV
  assumes is_ortho_set basisB
  assumes b. b  basisB  norm b = 1
  shows cspan {butterfly a b| (a::'a::{complex_normed_vector}) (b::'b::{chilbert_space,cfinite_dim}). a  basisA  b  basisB} = UNIV
proof -
  have F: F{butterfly a b |a b. a  basisA  b  basisB}. b'basisB. F *V b' = (if b' = b then a else 0)
    if a  basisA and b  basisB for a b
    apply (rule bexI[where x=butterfly a b])
    using assms that by (auto simp: is_ortho_set_def cnorm_eq_1)
  show ?thesis
    apply (rule cblinfun_cspan_UNIV[where basisA=basisB and basisB=basisA])
    using assms apply auto[2]
    using F by (smt (verit, ccfv_SIG) image_iff)
qed

lemma cindependent_butterfly:
  fixes basisA :: 'a::chilbert_space set and basisB :: 'b::chilbert_space set
  assumes is_ortho_set basisA is_ortho_set basisB
  assumes normA: a. abasisA  norm a = 1 and normB: b. bbasisB  norm b = 1
  shows cindependent {butterfly a b| a b. abasisA  bbasisB}
proof (unfold complex_vector.independent_explicit_module, intro allI impI, rename_tac T f g)
  fix T :: ('b CL 'a) set and f :: 'b CL 'a  complex and g :: 'b CL 'a
  assume finite T
  assume T_subset: T  {butterfly a b |a b. a  basisA  b  basisB}
  define lin where lin = (gT. f g *C g)
  assume lin = 0
  assume g  T
  (* To show: f g = 0 *)
  then obtain a b where g: g = butterfly a b and [simp]: a  basisA b  basisB
    using T_subset by auto

  have *: "(vector_to_cblinfun a)* *V f g *C g *V b = 0"
    if g  T - {butterfly a b} for g
  proof -
    from that
    obtain a' b' where g: g = butterfly a' b' and [simp]: a'  basisA b'  basisB
      using T_subset by auto
    from that have g  butterfly a b by auto
    with g consider (a) aa' | (b) bb'
      by auto
    then show (vector_to_cblinfun a)* *V f g *C g *V b = 0
    proof cases
      case a
      then show ?thesis
        using  is_ortho_set basisA unfolding g
        by (auto simp: is_ortho_set_def butterfly_def scaleC_cblinfun.rep_eq)
    next
      case b
      then show ?thesis
        using  is_ortho_set basisB unfolding g
        by (auto simp: is_ortho_set_def butterfly_def scaleC_cblinfun.rep_eq)
    qed
  qed

  have 0 = (vector_to_cblinfun a)* *V lin *V b
    using lin = 0 by auto
  also have  = (gT. (vector_to_cblinfun a)* *V (f g *C g) *V b)
    unfolding lin_def
    apply (rule complex_vector.linear_sum)
    by (smt (z3) cblinfun.scaleC_left cblinfun.scaleC_right cblinfun.add_right clinearI plus_cblinfun.rep_eq)
  also have  = (g{butterfly a b}. (vector_to_cblinfun a)* *V (f g *C g) *V b)
    apply (rule sum.mono_neutral_right)
    using finite T * g  T g by auto
  also have  = (vector_to_cblinfun a)* *V (f g *C g) *V b
    by (simp add: g)
  also have  = f g
    unfolding g
    using normA normB by (auto simp: butterfly_def scaleC_cblinfun.rep_eq cnorm_eq_1)
  finally show f g = 0
    by simp
qed

lemma clinear_eq_butterflyI:
  fixes F G :: ('a::{chilbert_space,cfinite_dim} CL 'b::complex_inner)  'c::complex_vector
  assumes "clinear F" and "clinear G"
  assumes cspan basisA = UNIV cspan basisB = UNIV
  assumes is_ortho_set basisA is_ortho_set basisB
  assumes "a b. abasisA  bbasisB  F (butterfly a b) = G (butterfly a b)"
  assumes b. bbasisB  norm b = 1
  shows "F = G"
  apply (rule complex_vector.linear_eq_on_span[where f=F, THEN ext, rotated 3])
     apply (subst cspan_butterfly_UNIV)
  using assms by auto

lemma sum_butterfly_is_Proj:
  assumes is_ortho_set E
  assumes e. eE  norm e = 1
  shows is_Proj (eE. butterfly e e)
proof (cases finite E)
  case True
  show ?thesis
  proof (rule is_Proj_I)
    show (eE. butterfly e e)* = (eE. butterfly e e)
      by (simp add: sum_adj)
    have ortho: f  e  e  E  f  E  is_orthogonal f e for f e
      by (meson assms(1) is_ortho_set_def)
    have unit: e C e = 1 if e  E for e
      using assms(2) cnorm_eq_1 that by blast
    have *: (fE. (f C e) *C butterfly f e) = butterfly e e if e  E for e
      apply (subst sum_single[where i=e])
      by (auto intro!: simp: that ortho unit True)
    show (eE. butterfly e e) oCL (eE. butterfly e e) = (eE. butterfly e e)
      by (auto simp: * cblinfun_compose_sum_right cblinfun_compose_sum_left)
  qed
next
  case False
  then show ?thesis
    by simp
qed

lemma rank1_compose_left: rank1 (a oCL b) if rank1 b
proof -
  from rank1 b
  obtain ψ where b *S  = ccspan {ψ}
    using rank1_def by blast
  then have *: (a oCL b) *S  = ccspan {a ψ}
    by (metis cblinfun_assoc_left(2) cblinfun_image_ccspan image_empty image_insert)
  then show rank1 (a oCL b)
    using rank1_def by blast
qed

lemma csubspace_of_1dim_space:
  assumes S  {0}
  assumes csubspace S
  assumes S  cspan {ψ}
  shows S = cspan {ψ}
proof -
  from S  {0} csubspace S
  obtain φ where φ  S and φ  0
    using complex_vector.subspace_0 by blast
  then have φ  cspan {ψ}
    using S  cspan {ψ} by blast
  with φ  0 obtain c where φ = c *C ψ and c  0
    by (metis complex_vector.span_breakdown_eq complex_vector.span_empty right_minus_eq scaleC_eq_0_iff singletonD)
  have cspan {ψ} = cspan {inverse c *C φ}
    by (simp add: φ = c *C ψ c  0)
  also have   cspan {φ}
    using c  0 by auto
  also from φ = c *C ψ φ  S c  0 assms
  have   S
    by (metis complex_vector.span_subspace cspan_singleton_scaleC empty_subsetI insert_Diff insert_mono)
  finally have cspan {ψ}  S
    by -
  with S  cspan {ψ} show ?thesis
    by simp
qed

lemma subspace_of_1dim_ccspan:
  assumes S  0
  assumes S  ccspan {ψ}
  shows S = ccspan {ψ}
  using assms apply transfer
  by (simp add: csubspace_of_1dim_space)

lemma rank1_compose_right: rank1 (a oCL b) if rank1 a
proof -
  have (a oCL b) *S   a *S 
    by (metis cblinfun_apply_cblinfun_compose cblinfun_apply_in_image cblinfun_image_ccspan_leqI ccspan_UNIV)
  also from rank1 a
  obtain ψ where a *S  = ccspan {ψ}
    using rank1_def by blast
  finally have *: (a oCL b) *S   ccspan {ψ}
    by -
  show rank1 (a oCL b)
  proof (cases (a oCL b) *S  = 0)
    case True
    then show ?thesis 
      by simp
  next
    case False
    with * have (a oCL b) *S  = ccspan {ψ}
      using subspace_of_1dim_ccspan by blast
    then show ?thesis
      using rank1_def by blast
  qed
qed
(*   obtain φ where ‹(a oCL b) *S ⊤›
  then have *: ‹(a oCL b) *S ⊤ ≤ ccspan {ψ}›
    by (metis cblinfun_assoc_left(2) cblinfun_image_mono top_greatest)
  obtain φ where φ_ab: ‹φ ∈ space_as_set ((a oCL b) *S ⊤)›
    by (metis cblinfun_apply_in_image)
  with * have ‹φ ∈ space_as_set (ccspan {ψ})›
    using less_eq_ccsubspace.rep_eq by blast
  then obtain c where ‹φ = c *C ψ›
    apply (simp add: ccspan.rep_eq)
    by (auto simp add: complex_vector.span_singleton)
  with φ_ab have ‹(a oCL b) *S ⊤ ≥ ccspan {ψ}›
    apply (auto intro!: ccspan_leqI simp: )
try0
sledgehammer [dont_slice]
  by (metis X ccspan_leqI ccspan_singleton_scaleC empty_subsetI insert_subset scaleC_eq_0_iff)
by -
    by (metis ccspan_leqI ccspan_singleton_scaleC empty_subsetI insert_subset)
  with * have ‹(a oCL b) *S ⊤ = ccspan {ψ}›
    by fastforce
  then show ‹rank1 (a oCL b)›
    using rank1_def by blast
qed *)

lemma rank1_scaleC: rank1 (c *C a) if rank1 a and c  0
  using rank1_compose_left[OF rank1 a, where a=c *C id_cblinfun]
  using that by force

lemma rank1_uminus: rank1 (-a) if rank1 a
  using that rank1_scaleC[where c=-1 and a=a] by simp

lemma rank1_uminus_iff[simp]: rank1 (-a)  rank1 a
  using rank1_uminus by force

lemma rank1_adj: rank1 (a*) if rank1 a
  for a :: 'a::chilbert_space CL 'b::chilbert_space
  by (metis adj_0 butterfly_adjoint rank1_iff_butterfly that)

lemma rank1_adj_iff[simp]: rank1 (a*)  rank1 a
  for a :: 'a::chilbert_space CL 'b::chilbert_space
  by (metis double_adj rank1_adj)

lemma butterflies_sum_id_finite: id_cblinfun = (xB. selfbutter x) if is_onb B for B :: 'a :: {cfinite_dim, chilbert_space} set
proof (rule cblinfun_eq_gen_eqI)
  from is_onb B show ccspan B = 
    by (simp add: is_onb_def)
  from is_onb B have cindependent B
    by (simp add: is_onb_def is_ortho_set_cindependent)
  then have [simp]: finite B
    using cindependent_cfinite_dim_finite by blast
  from is_onb B
  have 1: j  b  j  B  is_orthogonal j b if b  B for j b
    using that by (auto simp add: is_onb_def is_ortho_set_def)
  from is_onb B
  have 2: b C b = 1 if b  B for b
    using that by (simp add: is_onb_def cnorm_eq_1)
  fix b assume b  B
  then show id_cblinfun *V b = (xB. selfbutter x) *V b
    using 1 2 by (simp add: cblinfun.sum_left sum_single[where i=b])
qed

lemma butterfly_sum_left: butterfly (iM. ψ i) φ = (iM. butterfly (ψ i) φ)
  apply (induction M rule:infinite_finite_induct)
  by (auto simp add: butterfly_add_left)

lemma butterfly_sum_right: butterfly ψ (iM. φ i) = (iM. butterfly ψ (φ i))
  apply (induction M rule:infinite_finite_induct)
  by (auto simp add: butterfly_add_right)

subsection ‹Banach-Steinhaus›

theorem cbanach_steinhaus:
  fixes F :: 'c  'a::cbanach CL 'b::complex_normed_vector
  assumes x. M. n.  norm ((F n) *V x)  M
  shows  M.  n. norm (F n)  M
  using cblinfun_blinfun_transfer[transfer_rule]
  apply fail? (* Clears "current facts" *)
proof (use assms in transfer)
  fix F :: 'c  'a L 'b assume (x. M. n. norm (F n *v x)  M)
  hence x. bounded (range (λn. blinfun_apply (F n) x))
    by (metis (no_types, lifting) boundedI rangeE)
  hence bounded (range F)
    by (simp add: banach_steinhaus)
  thus  M. n. norm (F n)  M
    by (simp add: bounded_iff)
qed

subsection ‹Riesz-representation theorem›

theorem riesz_representation_cblinfun_existence:
  ― ‹Theorem 3.4 in citeconway2013course
  fixes f::'a::chilbert_space CL complex
  shows t. x.  f *V x = (t C x)
  by transfer (rule riesz_representation_existence)

lemma riesz_representation_cblinfun_unique:
  ― ‹Theorem 3.4 in citeconway2013course
  fixes f::'a::complex_inner CL complex
  assumes x. f *V x = (t C x)
  assumes x. f *V x = (u C x)
  shows t = u
  using assms by (rule riesz_representation_unique)

theorem riesz_representation_cblinfun_norm:
  includes notation_norm
  fixes f::'a::chilbert_space CL complex
  assumes x.  f *V x = (t C x)
  shows f = t
  using assms
proof transfer
  fix f::'a  complex and t
  assume bounded_clinear f and x. f x = (t C x)
  from  x. f x = (t C x)
  have (norm (f x)) / (norm x)  norm t
    for x
  proof(cases norm x = 0)
    case True
    thus ?thesis by simp
  next
    case False
    have norm (f x) = norm ((t C x))
      using x. f x = (t C x) by simp
    also have norm (t C x)  norm t * norm x
      by (simp add: complex_inner_class.Cauchy_Schwarz_ineq2)
    finally have norm (f x)  norm t * norm x
      by blast
    thus ?thesis
      by (metis False linordered_field_class.divide_right_mono nonzero_mult_div_cancel_right norm_ge_zero)
  qed
  moreover have (norm (f t)) / (norm t) = norm t
  proof(cases norm t = 0)
    case True
    thus ?thesis
      by simp
  next
    case False
    have f t = (t C t)
      using x. f x = (t C x) by blast
    also have  = (norm t)^2
      by (meson cnorm_eq_square)
    also have  = (norm t)*(norm t)
      by (simp add: power2_eq_square)
    finally have f t = (norm t)*(norm t)
      by blast
    thus ?thesis
      by (metis f t = t C t norm_eq_sqrt_cinner norm_ge_zero real_div_sqrt)
  qed
  ultimately have Sup {(norm (f x)) / (norm x)| x. True} = norm t
    by (smt cSup_eq_maximum mem_Collect_eq)
  moreover have Sup {(norm (f x)) / (norm x)| x. True} = (SUP x. (norm (f x)) / (norm x))
    by (simp add: full_SetCompr_eq)
  ultimately show onorm f = norm t
    by (simp add: onorm_def)
qed

definition the_riesz_rep :: ('a::chilbert_space CL complex)  'a where
  the_riesz_rep f = (SOME t. x. f *V x = t C x)

lemma the_riesz_rep[simp]: the_riesz_rep f C x = f *V x
  unfolding the_riesz_rep_def
  apply (rule someI2_ex)
  by (simp_all add: riesz_representation_cblinfun_existence)

lemma the_riesz_rep_unique:
  assumes x. f *V x = t C x
  shows t = the_riesz_rep f
  using assms riesz_representation_cblinfun_unique the_riesz_rep by metis

lemma the_riesz_rep_scaleC: the_riesz_rep (c *C f) = cnj c *C the_riesz_rep f
  apply (rule the_riesz_rep_unique[symmetric])
  by auto

lemma the_riesz_rep_add: the_riesz_rep (f + g) = the_riesz_rep f + the_riesz_rep g
  apply (rule the_riesz_rep_unique[symmetric])
  by (auto simp: cinner_add_left cblinfun.add_left)

lemma the_riesz_rep_norm[simp]: norm (the_riesz_rep f) = norm f
  apply (rule riesz_representation_cblinfun_norm[symmetric])
  by simp

lemma bounded_antilinear_the_riesz_rep[bounded_antilinear]: bounded_antilinear the_riesz_rep
  by (metis (no_types, opaque_lifting) bounded_antilinear_intro dual_order.refl mult.commute mult_1 the_riesz_rep_add the_riesz_rep_norm the_riesz_rep_scaleC)

lift_definition the_riesz_rep_sesqui :: ('a::complex_normed_vector  'b::chilbert_space  complex)  ('a CL 'b) is
  λp. if bounded_sesquilinear p then the_riesz_rep o CBlinfun o p else 0
  by (metis (mono_tags, lifting) Cblinfun_comp_bounded_sesquilinear bounded_antilinear_o_bounded_antilinear' bounded_antilinear_the_riesz_rep bounded_clinear_0 fun.map_comp)

lemma the_riesz_rep_sesqui_apply:
  assumes bounded_sesquilinear p
  shows (the_riesz_rep_sesqui p *V x) C y = p x y
  apply (transfer fixing: p x y)
  by (auto simp add: CBlinfun_inverse bounded_sesquilinear_apply_bounded_clinear assms)

subsection ‹Bidual›

lift_definition bidual_embedding :: 'a::complex_normed_vector CL (('a CL complex) CL complex)
  is λx f. f *V x
  by (simp add: cblinfun.flip)

lemma bidual_embedding_apply[simp]: (bidual_embedding *V x) *V f = f *V x
  by (transfer fixing: x f, simp)

lemma bidual_embedding_isometric[simp]: norm (bidual_embedding *V x) = norm x for x :: 'a::complex_inner
proof -
  define f :: 'a CL complex where f = CBlinfun (λy. cinner x y)
  then have [simp]: f *V y = cinner x y for y
    by (simp add: bounded_clinear_CBlinfun_apply bounded_clinear_cinner_right)
  then have [simp]: norm f = norm x
    apply (auto intro!: norm_cblinfun_eqI[where x=x] simp: power2_norm_eq_cinner[symmetric])
     apply (smt (verit, best) norm_eq_sqrt_cinner norm_ge_zero power2_norm_eq_cinner real_div_sqrt)
    using Cauchy_Schwarz_ineq2 by blast
  show ?thesis
    apply (auto intro!: norm_cblinfun_eqI[where x=f])
     apply (metis norm_eq_sqrt_cinner norm_imp_pos_and_ge real_div_sqrt)
    by (metis norm_cblinfun ordered_field_class.sign_simps(33))
qed

lemma norm_bidual_embedding[simp]: norm (bidual_embedding :: 'a::{complex_inner, not_singleton} CL _) = 1
proof -
  obtain x :: 'a where [simp]: norm x = 1
    by (meson UNIV_not_singleton ex_norm1)
  show ?thesis
    by (auto intro!: norm_cblinfun_eqI[where x=x])
qed

lemma isometry_bidual_embedding[simp]: isometry bidual_embedding
  by (simp add: norm_preserving_isometry)

lemma bidual_embedding_surj[simp]: surj (bidual_embedding :: 'a::chilbert_space CL _)
proof -
  have y''. f. (bidual_embedding *V y'') *V f = y *V f
    for y :: ('a CL complex) CL complex
  proof -
    define y' where y' = CBlinfun (λz. cnj (y (cblinfun_cinner_right z)))
    have y'_apply: y' z = cnj (y (cblinfun_cinner_right z)) for z
      unfolding y'_def
      apply (subst CBlinfun_inverse)
      by (auto intro!: bounded_linear_intros)
    obtain y'' where y' z = y'' C z for z
      using riesz_representation_cblinfun_existence by blast
    then have y'': z C y'' = cnj (y' z) for z
      by auto
    have (bidual_embedding *V y'') *V f = y *V f for f :: 'a CL complex
    proof -
      obtain f' where f': f z = f' C z for z
        using riesz_representation_cblinfun_existence by blast
      then have f'2: f = cblinfun_cinner_right f'
        using cblinfun_apply_inject by force
      show ?thesis
        by (auto simp add: f' f'2 y'' y'_apply)
    qed
    then show ?thesis
      by blast
  qed
  then show ?thesis
    by (metis cblinfun_eqI surj_def)
qed

subsection ‹Extension of complex bounded operators›

definition cblinfun_extension where
  "cblinfun_extension S φ = (SOME B. xS. B *V x = φ x)"

definition cblinfun_extension_exists where
  "cblinfun_extension_exists S φ = (B. xS. B *V x = φ x)"

lemma cblinfun_extension_existsI:
  assumes "x. xS  B *V x = φ x"
  shows "cblinfun_extension_exists S φ"
  using assms cblinfun_extension_exists_def by blast

lemma cblinfun_extension_exists_finite_dim:
  fixes φ::"'a::{complex_normed_vector,cfinite_dim}  'b::complex_normed_vector"
  assumes "cindependent S"
    and "cspan S = UNIV"
  shows "cblinfun_extension_exists S φ"
proof-
  define f::"'a  'b"
    where "f = complex_vector.construct S φ"
  have "clinear f"
    by (simp add: complex_vector.linear_construct assms linear_construct f_def)
  have "bounded_clinear f"
    using clinear f assms by auto
  then obtain B::"'a CL 'b"
    where "B *V x = f x" for x
    using cblinfun_apply_cases by blast
  have "B *V x = φ x"
    if c1: "xS"
    for x
  proof-
    have "B *V x = f x"
      by (simp add: x. B *V x = f x)
    also have " = φ x"
      using assms complex_vector.construct_basis f_def that
      by (simp add: complex_vector.construct_basis)
    finally show?thesis by blast
  qed
  thus ?thesis
    unfolding cblinfun_extension_exists_def
    by blast
qed

lemma cblinfun_extension_apply:
  assumes "cblinfun_extension_exists S f"
    and "v  S"
  shows "(cblinfun_extension S f) *V v = f v"
  by (smt assms cblinfun_extension_def cblinfun_extension_exists_def tfl_some)

lemma
  fixes f :: 'a::complex_normed_vector  'b::cbanach
  assumes csubspace S
  assumes closure S = UNIV
  assumes f_add: x y. x  S  y  S  f (x + y) = f x + f y
  assumes f_scale: c x y. x  S  f (c *C x) = c *C f x
  assumes bounded: x. x  S  norm (f x)  B * norm x
  shows cblinfun_extension_exists_bounded_dense: cblinfun_extension_exists S f
    and cblinfun_extension_norm_bounded_dense: B  0  norm (cblinfun_extension S f)  B
proof -
  define B' where B' = (if B0 then 1 else B)
  then have bounded': x. x  S  norm (f x)  B' * norm x
    using bounded by (metis mult_1 mult_le_0_iff norm_ge_zero order_trans)
  have B' > 0
    by (simp add: B'_def)

  have xi. (xi  x)  (i. xi i  S) for x
    using assms(2) closure_sequential by blast
  then obtain seq :: 'a  nat  'a where seq_lim: seq x  x and seq_S: seq x i  S for x i
    apply (atomize_elim, subst all_conj_distrib[symmetric])
    apply (rule choice)
    by auto
  define g where g x = lim (λi. f (seq x i)) for x
  have Cauchy (λi. f (seq x i)) for x
  proof (rule CauchyI)
    fix e :: real assume e > 0
    have Cauchy (seq x)
      using LIMSEQ_imp_Cauchy seq_lim by blast
    then obtain M where less_eB: norm (seq x m - seq x n) < e/B' if n  M and m  M for n m
      by atomize_elim (meson CauchyD 0 < B' 0 < e linordered_field_class.divide_pos_pos)
    have norm (f (seq x m) - f (seq x n)) < e if n  M and m  M for n m
    proof -
      have norm (f (seq x m) - f (seq x n)) = norm (f (seq x m - seq x n))
        using f_add f_scale seq_S
        by (metis add_diff_cancel assms(1) complex_vector.subspace_diff diff_add_cancel)
      also have   B' * norm (seq x m - seq x n)
        apply (rule bounded')
        by (simp add: assms(1) complex_vector.subspace_diff seq_S)
      also from less_eB have  < B' * (e/B')
        by (meson 0 < B' linordered_semiring_strict_class.mult_strict_left_mono that)
      also have   e
        using 0 < B' by auto
      finally show ?thesis
        by -
    qed
    then show M. mM. nM. norm (f (seq x m) - f (seq x n)) < e
      by auto
  qed
  then have f_seq_lim: (λi. f (seq x i))  g x for x
    by (simp add: Cauchy_convergent_iff convergent_LIMSEQ_iff g_def)
  have f_xi_lim: (λi. f (xi i))  g x if xi  x and i. xi i  S for xi x
  proof -
    from seq_lim that
    have (λi. B' * norm (xi i - seq x i))  0
      by (metis (no_types) 0 < B' cancel_comm_monoid_add_class.diff_cancel norm_not_less_zero norm_zero tendsto_diff tendsto_norm_zero_iff tendsto_zero_mult_left_iff)
    then have (λi. f (xi i + (-1) *C seq x i))  0
      apply (rule Lim_null_comparison[rotated])
      using bounded' by (simp add: assms(1) complex_vector.subspace_diff seq_S that(2))
    then have (λi. f (xi i) - f (seq x i))  0
      apply (subst (asm) f_add)
        apply (auto simp: that csubspace S complex_vector.subspace_neg seq_S)[2]
      apply (subst (asm) f_scale)
      by (auto simp: that csubspace S complex_vector.subspace_neg seq_S)
    then show (λi. f (xi i))  g x
      using Lim_transform f_seq_lim by fastforce
  qed
  have g_add: g (x + y) = g x + g y for x y
  proof -
    obtain xi :: nat  'a where xi  x and xi i  S for i
      using seq_S seq_lim by auto
    obtain yi :: nat  'a where yi  y and yi i  S for i
      using seq_S seq_lim by auto
    have (λi. xi i + yi i)  x + y
      using xi  x yi  y tendsto_add by blast
    then have lim1: (λi. f (xi i + yi i))  g (x + y)
      by (simp add: i. xi i  S i. yi i  S assms(1) complex_vector.subspace_add f_xi_lim)
    have (λi. f (xi i + yi i)) = (λi. f (xi i) + f (yi i))
      by (simp add: i. xi i  S i. yi i  S f_add)
    also have   g x + g y
      by (simp add: i. xi i  S i. yi i  S xi  x yi  y f_xi_lim tendsto_add)
    finally show ?thesis
      using lim1 LIMSEQ_unique by blast
  qed
  have g_scale: g (c *C x) = c *C g x for c x
  proof -
    obtain xi :: nat  'a where xi  x and xi i  S for i
      using seq_S seq_lim by auto
    have (λi. c *C xi i)  c *C x
      using xi  x bounded_clinear_scaleC_right clinear_continuous_at isCont_tendsto_compose by blast
    then have lim1: (λi. f (c *C xi i))  g (c *C x)
      by (simp add: i. xi i  S assms(1) complex_vector.subspace_scale f_xi_lim)
    have (λi. f (c *C xi i)) = (λi. c *C f (xi i))
      by (simp add: i. xi i  S f_scale)
    also have   c *C g x
      using i. xi i  S xi  x bounded_clinear_scaleC_right clinear_continuous_at f_xi_lim isCont_tendsto_compose by blast
    finally show ?thesis
      using lim1 LIMSEQ_unique by blast
  qed

  have [simp]: f x = g x if x  S for x
  proof -
    have (λ_. x)  x
      by auto
    then have (λ_. f x)  g x
      using that by (rule f_xi_lim)
    then show f x = g x
      by (simp add: LIMSEQ_const_iff)
  qed

  have g_bounded: norm (g x)  B' * norm x for x
  proof -
    obtain xi :: nat  'a where xi  x and xi i  S for i
      using seq_S seq_lim by auto
    then have (λi. f (xi i))  g x
      using f_xi_lim by presburger
    then have (λi. norm (f (xi i)))  norm (g x)
      by (metis tendsto_norm)
    moreover have (λi. B' * norm (xi i))  B' * norm x
      by (simp add: xi  x tendsto_mult_left tendsto_norm)
    ultimately show norm (g x)  B' * norm x
      apply (rule lim_mono[rotated])
      using bounded' using xi _  S by blast
  qed

  have bounded_clinear g
    using g_add g_scale apply (rule bounded_clinearI[where K=B'])
    using g_bounded by (simp add: ordered_field_class.sign_simps(5))
  then have [simp]: CBlinfun g *V x = g x for x
    by (subst CBlinfun_inverse, auto)

  show cblinfun_extension_exists S f
    apply (rule cblinfun_extension_existsI[where B=CBlinfun g])
    by auto

  then have cblinfun_extension S f *V ψ = CBlinfun g *V ψ if ψ  S for ψ
    by (simp add: cblinfun_extension_apply that)

  then have ext_is_g: (*V) (cblinfun_extension S f) = g
    apply (rule_tac ext)
    apply (rule on_closure_eqI[where S=S])
    using  closure S = UNIV bounded_clinear g
    by (auto simp add: continuous_at_imp_continuous_on clinear_continuous_within)

  show norm (cblinfun_extension S f)  B if B  0
  proof (cases B > 0)
    case True
    then have B = B'
      unfolding B'_def
      by auto
    moreover have *: norm (cblinfun_extension S f)  B'
      by (metis ext_is_g 0 < B' g_bounded norm_cblinfun_bound order_le_less)
    ultimately show ?thesis
      by simp
  next
    case False
    with bounded have f x = 0 if x  S for x
      by (smt (verit) mult_nonpos_nonneg norm_ge_zero norm_le_zero_iff that)
    then have g x = (λ_. 0) x if x  S for x
      using that by simp
    then have g x = 0 for x
      apply (rule on_closure_eqI[where S=S])
      using closure S = UNIV bounded_clinear g
      by (auto simp add: continuous_at_imp_continuous_on clinear_continuous_within)
    with ext_is_g have cblinfun_extension S f = 0
      by (simp add: cblinfun_eqI)
    then show ?thesis
      using that by simp
  qed
qed

lemma cblinfun_extension_cong:
  assumes cspan A = cspan B
  assumes B  A
  assumes fg: x. xB  f x = g x
  assumes cblinfun_extension_exists A f
  shows cblinfun_extension A f = cblinfun_extension B g
proof -
  from cblinfun_extension_exists A f fg B  A
  have cblinfun_extension_exists B g
    by (metis assms(2) cblinfun_extension_exists_def subset_eq)

  have (xA. C *V x = f x)  (xB. C *V x = f x) for C
    by (smt (verit, ccfv_SIG) assms(1) assms(2) assms(4) cblinfun_eq_on_span cblinfun_extension_exists_def complex_vector.span_eq subset_iff)
  also from fg have  C  (xB. C *V x = g x) for C
    by auto
  finally show cblinfun_extension A f = cblinfun_extension B g
    unfolding cblinfun_extension_def
    by auto
qed

lemma
  fixes f :: 'a::complex_inner  'b::chilbert_space and S 
  assumes is_ortho_set S and closure (cspan S) = UNIV
  assumes ortho_f: x y. xS  yS  xy  is_orthogonal (f x) (f y)
  assumes bounded: x. x  S  norm (f x)  B * norm x
  shows cblinfun_extension_exists_ortho: cblinfun_extension_exists S f
    and cblinfun_extension_exists_ortho_norm: B  0  norm (cblinfun_extension S f)  B
proof -
  define g where g = cconstruct S f
  have cindependent S
    using assms(1) is_ortho_set_cindependent by blast
  have g_f: g x = f x if xS for x
    unfolding g_def using cindependent S that by (rule complex_vector.construct_basis)
  have [simp]: clinear g
    unfolding g_def using cindependent S by (rule complex_vector.linear_construct)
  then have g_add: g (x + y) = g x + g y if x  cspan S and y  cspan S for x y
    using clinear_iff by blast
  from clinear g have g_scale: g (c *C x) = c *C g x if x  cspan S for x c
    by (simp add: complex_vector.linear_scale)
  moreover have g_bounded: norm (g x)  abs B * norm x if x  cspan S for x
  proof -
    from that obtain t r where x_sum: x = (at. r a *C a) and finite t and t  S
      unfolding complex_vector.span_explicit by auto
    have (norm (g x))2 = (norm (at. r a *C g a))2
      by (simp add: x_sum complex_vector.linear_sum clinear.scaleC)
    also have  = (norm (at. r a *C f a))2
      by (smt (verit) t  S g_f in_mono sum.cong)
    also have  = (at. (norm (r a *C f a))2)
      using _ finite t apply (rule pythagorean_theorem_sum)
      using t  S ortho_f in_mono by fastforce
    also have  = (at. (cmod (r a) * norm (f a))2)
      by simp
    also have   (at. (cmod (r a) * B * norm a)2)
      apply (rule sum_mono)
      by (metis t  S assms(4) in_mono mult_left_mono mult_nonneg_nonneg norm_ge_zero power_mono vector_space_over_itself.scale_scale)
    also have  = B2 * (at. (norm (r a *C a))2)
      by (simp add: sum_distrib_left mult.commute vector_space_over_itself.scale_left_commute flip: power_mult_distrib)
    also have  = B2 * (norm (at. (r a *C a)))2
      apply (subst pythagorean_theorem_sum)
      using finite t by auto (meson t  S assms(1) is_ortho_set_def subsetD)
    also have  = (abs B * norm x)2
      by (simp add: power_mult_distrib x_sum)
    finally show norm (g x)  abs B * norm x
      by auto
  qed
  
  from g_add g_scale g_bounded
  have extg_exists: cblinfun_extension_exists (cspan S) g
    apply (rule_tac cblinfun_extension_exists_bounded_dense[where B=abs B])
    using closure (cspan S) = UNIV by auto

  then show cblinfun_extension_exists S f
    by (metis (mono_tags, opaque_lifting) g_f cblinfun_extension_apply cblinfun_extension_existsI complex_vector.span_base)

  have norm_extg: norm (cblinfun_extension (cspan S) g)  B if B  0
    apply (rule cblinfun_extension_norm_bounded_dense)
    using g_add g_scale g_bounded closure (cspan S) = UNIV that by auto

  have extg_extf: cblinfun_extension (cspan S) g = cblinfun_extension S f
    apply (rule cblinfun_extension_cong)
    by (auto simp add: complex_vector.span_base g_f extg_exists)

  from norm_extg extg_extf
  show norm (cblinfun_extension S f)  B if B  0
    using that by simp
qed


lemma cblinfun_extension_exists_proj:
  fixes f :: 'a::complex_normed_vector  'b::cbanach
  assumes csubspace S
  assumes ex_P: P :: 'a CL 'a. is_Proj P  range P = closure S
  assumes f_add: x y. x  S  y  S  f (x + y) = f x + f y
  assumes f_scale: c x y. x  S  f (c *C x) = c *C f x
  assumes bounded: x. x  S  norm (f x)  B * norm x
  shows cblinfun_extension_exists S f
    ― ‹We cannot give a statement about the norm. While there is an extension with norm termB,
        there is no guarantee that termcblinfun_extension S f returns that specific extension since
        the extension is only determined on termccspan S.›
proof (cases B  0)
  case True
  note True[simp]
  obtain P :: 'a CL 'a where P_proj: is_Proj P and P_im: range P = closure S
    using ex_P by blast 
  define f' S' where f' ψ = f (P ψ) and S' = S + space_as_set (kernel P) for ψ
  have PS': P *V x  S if x  S' for x
  proof -
    obtain x1 x2 where x12: x = x1 + x2 and x1: x1  S and x2: x2  space_as_set (kernel P)
      using that  S'_def x  S' set_plus_elim by blast
    have P *V x = P *V x1
      using x2 by (simp add: x12 cblinfun.add_right kernel_memberD)
    also have  = x1
      by (metis (no_types, opaque_lifting) P_im P_proj cblinfun_apply_cblinfun_compose closure_insert image_iff insertI1 insert_absorb is_Proj_idempotent x1)
    also have   S
      by (fact x1)
    finally show ?thesis
      by -
  qed
  have SS': S  S'
    by (metis S'_def ordered_field_class.sign_simps(2) set_zero_plus2 zero_space_as_set)

  have csubspace S'
    by (simp add: S'_def assms(1) csubspace_set_plus)
  moreover have closure S' = UNIV
  proof auto
    fix ψ
    have ψ = P ψ + (id - P) ψ
      by simp
    also have   closure S + space_as_set (kernel P)
      by (smt (verit) P_im P_proj calculation cblinfun.real.add_right eq_add_iff is_Proj_idempotent kernel_memberI rangeI set_plus_intro simp_a_oCL_b')
    also have   closure (closure S + space_as_set (kernel P))
      using closure_subset by blast
    also have  = closure (S + space_as_set (kernel P))
      using closed_sum_closure_left closed_sum_def by blast
    also have  = closure S'
      using S'_def by fastforce
    finally show ψ  closure S'
      by -
  qed

  moreover have f' (x + y) = f' x + f' y if x  S' and y  S' for x y
    using that by (auto simp: f'_def cblinfun.add_right f_add PS')
  moreover have f' (c *C x) = c *C f' x if x  S' for c x
    using that by (auto simp: f'_def cblinfun.scaleC_right f_scale PS')
  moreover 
  have norm (f' x)  (B * norm P) * norm x if x  S' for x
  proof -
    have norm (f' x)  B* norm (P x)
      by (auto simp: f'_def PS' bounded that)
    also have   B * norm P * norm x
      by (simp add: mult.assoc mult_left_mono norm_cblinfun)
    finally show ?thesis
      by auto
  qed

  ultimately have F_ex: cblinfun_extension_exists S' f'
    by (rule cblinfun_extension_exists_bounded_dense)
  define F where F = cblinfun_extension S' f'
  have F ψ = f ψ if ψ  S for ψ
  proof -
    from F_ex that SS' have F ψ = f' ψ
      by (auto simp add: F_def cblinfun_extension_apply)
    also have  = f (P *V ψ)
      by (simp add: f'_def)
    also have  = f ψ
      using P_proj P_im
      apply (transfer fixing: ψ S f)
      by (metis closure_subset in_mono is_projection_on_fixes_image is_projection_on_image that)
    finally show ?thesis
      by -
  qed
  then show cblinfun_extension_exists S f
    using cblinfun_extension_exists_def by blast
next
  case False
  then have S  {0}
    using bounded by auto (meson norm_ge_zero norm_le_zero_iff order_trans zero_le_mult_iff)
  then show cblinfun_extension_exists S f
    apply (rule_tac cblinfun_extension_existsI[where B=0])
    apply auto
    using bounded by fastforce
qed

lemma cblinfun_extension_exists_hilbert:
  fixes f :: 'a::chilbert_space  'b::cbanach
  assumes csubspace S
  assumes f_add: x y. x  S  y  S  f (x + y) = f x + f y
  assumes f_scale: c x y. x  S  f (c *C x) = c *C f x
  assumes bounded: x. x  S  norm (f x)  B * norm x
  shows cblinfun_extension_exists S f
    ― ‹We cannot give a statement about the norm. While there is an extension with norm termB,
        there is no guarantee that termcblinfun_extension S f returns that specific extension since
        the extension is only determined on termccspan S.›
proof -
  have P. is_Proj P  range ((*V) P) = closure S
    apply (rule exI[of _ Proj (ccspan S)])
    apply (rule conjI)
     by simp (metis Proj_is_Proj Proj_range Proj_range_closed assms(1) cblinfun_image.rep_eq ccspan.rep_eq closure_closed complex_vector.span_eq_iff space_as_set_top)

  from assms(1) this assms(2-4)
  show ?thesis
    by (rule cblinfun_extension_exists_proj)
qed

lemma cblinfun_extension_exists_restrict:
  assumes B  A
  assumes x. xB  f x = g x
  assumes cblinfun_extension_exists A f
  shows cblinfun_extension_exists B g
  by (metis assms cblinfun_extension_exists_def subset_eq)

subsection ‹Bijections between different ONBs›

text ‹Some of the theorems here logically belong into theoryComplex_Bounded_Operators.Complex_Inner_Product
  but the proof uses some concepts from the present theory.›

lemma all_ortho_bases_same_card:
  ― ‹Follows citeconway2013course, Proposition 4.14›
  fixes E F :: 'a::chilbert_space set
  assumes is_ortho_set E is_ortho_set F ccspan E =  ccspan F = 
  shows f. bij_betw f E F
proof -
  have |F| ≤o |E| if infinite E and
    is_ortho_set E is_ortho_set F ccspan E = top ccspan F = top for E F :: 'a set
  proof -
    define F' where F' e = {fF. ¬ is_orthogonal f e} for e
    have eE. cinner f e  0 if f  F for f
    proof (rule ccontr, simp)
      assume eE. is_orthogonal f e
      then have f  orthogonal_complement E
        by (simp add: orthogonal_complementI)
      also have  = orthogonal_complement (closure (cspan E))
        using orthogonal_complement_of_closure orthogonal_complement_of_cspan by blast
      also have  = space_as_set (- ccspan E)
        by transfer simp
      also have  = space_as_set (- top)
        by (simp add: ccspan E = top)
      also have  = {0}
        by (auto simp add: top_ccsubspace.rep_eq uminus_ccsubspace.rep_eq)
      finally have f = 0
        by simp
      with f  F is_ortho_set F show False
        by (simp add: is_onb_def is_ortho_set_def)
    qed
    then have F'_union: F = (eE. F' e)
      unfolding F'_def by auto
    have countable (F' e) for e
    proof -
      have (fM. (cmod (cinner (sgn f) e))2)  (norm e)2 if finite M and M  F for M
      proof -
        have [simp]: is_ortho_set M
          using is_ortho_set F is_ortho_set_antimono that(2) by blast
        have [simp]: norm (sgn f) = 1 if f  M for f
          by (metis is_ortho_set M is_ortho_set_def norm_sgn that)
        have (fM. (cmod (cinner (sgn f) e))2) = (fM. (norm ((cinner (sgn f) e) *C sgn f))2)
          apply (rule sum.cong[OF refl])
          by simp
        also have  = (norm (fM. ((cinner (sgn f) e) *C sgn f)))2
          apply (rule pythagorean_theorem_sum[symmetric])
          using that by auto (meson is_ortho_set M is_ortho_set_def)
        also have  = (norm (fM. proj f e))2
          by (metis butterfly_apply butterfly_sgn_eq_proj)
        also have  = (norm (Proj (ccspan M) e))2
          apply (rule arg_cong[where f=λx. (norm x)2])
          using finite M is_ortho_set M apply induction
           by simp (smt (verit, ccfv_threshold) Proj_orthog_ccspan_insert insertCI is_ortho_set_def plus_cblinfun.rep_eq sum.insert)
        also have   (norm (Proj (ccspan M)) * norm e)2
          by (auto simp: norm_cblinfun intro!: power_mono)
        also have   (norm e)2
          apply (rule power_mono)
           apply (metis norm_Proj_leq1 mult_left_le_one_le norm_ge_zero)
          by simp
        finally show ?thesis
          by -
      qed
      then have (λf. (cmod (cinner (sgn f) e))2) abs_summable_on F
        apply (intro nonneg_bdd_above_summable_on bdd_aboveI)
        by auto
      then have countable {f  F. (cmod (sgn f C e))2  0}
        by (rule abs_summable_countable)
      then have countable {f  F. ¬ is_orthogonal (sgn f) e}
        by force
      then have countable {f  F. ¬ is_orthogonal f e}
        by force
      then show ?thesis
        unfolding F'_def by simp
    qed
    then have F'_leq: |F' e| ≤o natLeq for e
      using countable_leq_natLeq by auto

    from F'_union have |F| ≤o |Sigma E F'| (is _ ≤o )
      using card_of_UNION_Sigma by blast
    also have  ≤o |E × (UNIV::nat set)| (is _ ≤o )
      apply (rule card_of_Sigma_mono1)
      using F'_leq
      using card_of_nat ordIso_symmetric ordLeq_ordIso_trans by blast
    also have  =o |E| *c natLeq (is ordIso2 _ )
      by (metis Field_card_of Field_natLeq card_of_ordIso_subst cprod_def)
    also have  =o |E|
      apply (rule card_prod_omega)
      using that by (simp add: cinfinite_def)
    finally show |F| ≤o |E|
      by -
  qed
  then have infinite: |E| =o |F| if infinite E and infinite F
    by (simp add: assms ordIso_iff_ordLeq that(1) that(2))

  have |E| =o |F| if finite E and is_ortho_set E is_ortho_set F ccspan E = top ccspan F = top
    for E F :: 'a set
  proof -
    have card E = card F
      using that 
      by (metis bij_betw_same_card ccspan.rep_eq closure_finite_cspan complex_vector.bij_if_span_eq_span_bases 
          complex_vector.independent_span_bound is_ortho_set_cindependent top_ccsubspace.rep_eq top_greatest)
    with finite E have finite F
      by (metis ccspan.rep_eq closure_finite_cspan complex_vector.independent_span_bound is_ortho_set_cindependent that(3) that(4) top_ccsubspace.rep_eq top_greatest)
    with card E = card F that show ?thesis
      apply (rule_tac finite_card_of_iff_card[THEN iffD2])
      by auto
  qed

  with infinite assms have |E| =o |F|
    by (meson ordIso_symmetric)

  then show ?thesis
    by (simp add: card_of_ordIso)
qed

lemma all_onbs_same_card:
  fixes E F :: 'a::chilbert_space set
  assumes is_onb E is_onb F
  shows f. bij_betw f E F
  apply (rule all_ortho_bases_same_card)
  using assms by (auto simp: is_onb_def)

definition bij_between_bases where bij_between_bases E F = (SOME f. bij_betw f E F) for E F :: 'a::chilbert_space set

lemma bij_between_bases_bij:
  fixes E F :: 'a::chilbert_space set
  assumes is_onb E is_onb F
  shows bij_betw (bij_between_bases E F) E F
  using all_onbs_same_card
  by (metis assms(1) assms(2) bij_between_bases_def someI)

definition unitary_between where unitary_between E F = cblinfun_extension E (bij_between_bases E F)

lemma unitary_between_apply:
  fixes E F :: 'a::chilbert_space set
  assumes is_onb E is_onb F e  E
  shows unitary_between E F *V e = bij_between_bases E F e
proof -
  from is_onb E is_onb F
  have EF: bij_between_bases E F e  F if e  E for e
    by (meson bij_betwE bij_between_bases_bij that)
  have ortho: is_orthogonal (bij_between_bases E F x) (bij_between_bases E F y) if x  y and x  E and y  E for x y
    by (smt (verit, del_insts) assms(1) assms(2) bij_betw_iff_bijections bij_between_bases_bij is_onb_def is_ortho_set_def that(1) that(2) that(3))
  have spanE: closure (cspan E) = UNIV
    by (metis assms(1) ccspan.rep_eq is_onb_def top_ccsubspace.rep_eq)
  show ?thesis
    unfolding unitary_between_def
    apply (rule cblinfun_extension_apply)
     apply (rule cblinfun_extension_exists_ortho[where B=1])
    using assms EF ortho spanE
    by (auto simp: is_onb_def)
qed

lemma unitary_between_unitary:
  fixes E F :: 'a::chilbert_space set
  assumes is_onb E is_onb F
  shows unitary (unitary_between E F)
proof -
  have (unitary_between E F *V b) C (unitary_between E F *V c) = b C c if b  E and c  E for b c
  proof (cases b = c)
    case True
    from is_onb E that have 1: b C b = 1
      using cnorm_eq_1 is_onb_def by blast
    from that have unitary_between E F *V b  F
      by (metis assms(1) assms(2) bij_betw_apply bij_between_bases_bij unitary_between_apply)
    with is_onb F have 2: (unitary_between E F *V b) C (unitary_between E F *V b) = 1
      by (simp add: cnorm_eq_1 is_onb_def)
    from 1 2 True show ?thesis
      by simp
  next
    case False
    from is_onb E that have 1: b C c = 0
      by (simp add: False is_onb_def is_ortho_set_def)
    from that have inF: unitary_between E F *V b  F unitary_between E F *V c  F
      by (metis assms(1) assms(2) bij_betw_apply bij_between_bases_bij unitary_between_apply)+
    have neq: unitary_between E F *V b  unitary_between E F *V c
      by (metis (no_types, lifting) False assms(1) assms(2) bij_betw_iff_bijections bij_between_bases_bij that(1) that(2) unitary_between_apply)
    from inF neq is_onb F have 2: (unitary_between E F *V b) C (unitary_between E F *V c) = 0
      by (simp add: is_onb_def is_ortho_set_def)
    from 1 2 show ?thesis
      by simp
  qed
  then have iso: isometry (unitary_between E F)
    apply (rule_tac orthogonal_on_basis_is_isometry[where B=E])
    using assms(1) is_onb_def by auto
  have unitary_between E F *S top = unitary_between E F *S ccspan E
    by (metis assms(1) is_onb_def)
  also have   ccspan (unitary_between E F ` E) (is _  )
    by (simp add: cblinfun_image_ccspan)
  also have  = ccspan (bij_between_bases E F ` E)
    by (metis assms(1) assms(2) image_cong unitary_between_apply)
  also have  = ccspan F
    by (metis assms(1) assms(2) bij_betw_imp_surj_on bij_between_bases_bij)
  also have  = top
    using assms(2) is_onb_def by blast
  finally have surj: unitary_between E F *S top = top
    by (simp add: top.extremum_unique)
  from iso surj show ?thesis
    by (rule surj_isometry_is_unitary)
qed


subsection ‹Notation›

bundle cblinfun_notation begin
notation cblinfun_compose (infixl "oCL" 67)
notation cblinfun_apply (infixr "*V" 70)
notation cblinfun_image (infixr "*S" 70)
notation adj ("_*" [99] 100)
type_notation cblinfun ("(_ CL /_)" [22, 21] 21)
end

bundle no_cblinfun_notation begin
no_notation cblinfun_compose (infixl "oCL" 67)
no_notation cblinfun_apply (infixr "*V" 70)
no_notation cblinfun_image (infixr "*S" 70)
no_notation adj ("_*" [99] 100)
no_type_notation cblinfun ("(_ CL /_)" [22, 21] 21)
end

unbundle no_cblinfun_notation
unbundle no_lattice_syntax

end