Theory Axioms_Complement_Quantum

section ‹Quantum instantiation of complements›

theory Axioms_Complement_Quantum
  imports
    Laws_Quantum
    Quantum_Extra
    Hilbert_Space_Tensor_Product.Weak_Star_Topology
    Hilbert_Space_Tensor_Product.Partial_Trace
    With_Type.With_Type
    Hilbert_Space_Tensor_Product.Misc_Tensor_Product_TTS
begin

unbundle no m_inv_syntax
unbundle cblinfun_syntax
unbundle lattice_syntax
unbundle register_syntax
no_notation Lattice.join (infixl "ı" 65)
no_notation elt_set_eq (infix "=o" 50)
no_notation eq_closure_of ("closure'_ofı")
hide_const (open) Order.top
declare [[eta_contract]]












(* (* TODO: can we use a generic rule similar to sum_parametric' instead? *)
lemma has_sum_weak_star_transfer[transfer_rule]:
  includes lifting_syntax
  fixes R :: ‹'a ⇒ 'b ⇒ bool›
  assumes [transfer_rule]: ‹bi_unique R›
  shows ‹((R ===> cr_cblinfun_weak_star) ===> (rel_set R) ===> cr_cblinfun_weak_star ===> (⟷)) (has_sum_in weak_star_topology) has_sum›
  unfolding has_sum_euclidean_iff[symmetric]
  by transfer_prover
 *)

(* lemma summable_on_weak_star_transfer[transfer_rule]:
  includes lifting_syntax
  fixes R :: ‹'a ⇒ 'b ⇒ bool›
  assumes [transfer_rule]: ‹bi_unique R›
  shows ‹((R ===> cr_cblinfun_weak_star) ===> (rel_set R) ===> (⟷)) (summable_on_in weak_star_topology) Infinite_Sum.summable_on›
(* TODO: can we use a generic rule similar to sum_parametric' instead? *)
  unfolding summable_on_def summable_on_in_def
  by transfer_prover
 *)

(* lemma infsum_weak_star_transfer[transfer_rule]:
  includes lifting_syntax
  fixes R :: ‹'a ⇒ 'b ⇒ bool›
  assumes [transfer_rule]: ‹bi_unique R›
  shows ‹((R ===> cr_cblinfun_weak_star) ===> (rel_set R) ===> cr_cblinfun_weak_star) (infsum_in weak_star_topology) infsum›
(* TODO: can we use a generic rule similar to sum_parametric' instead? *)
proof (intro rel_funI, rename_tac f g A B)
  fix f :: ‹'a ⇒ 'c ⇒CL 'd› and g A B
  assume [transfer_rule]: ‹(R ===> cr_cblinfun_weak_star) f g›
  assume [transfer_rule]: ‹rel_set R A B›
  show ‹cr_cblinfun_weak_star (infsum_in Weak_Star_Topology.weak_star_topology f A) (infsum g B)›
  proof (cases ‹g summable_on B›)
    case True
    then have True': ‹summable_on_in weak_star_topology f A›
      apply transfer by simp
    define a b where ‹a = infsum_in weak_star_topology f A› and ‹b = infsum g B›
    from True' have 1: ‹has_sum_in weak_star_topology f A a›
      by (simp add: a_def has_sum_in_infsum_in)
    from True have ‹has_sum g B b›
      using b_def summable_iff_has_sum_infsum by blast
    then have 2: ‹b' = b› if ‹has_sum g B b'› for b'
      using infsumI that by blast
    from 1 2 show ‹cr_cblinfun_weak_star a b›
      unfolding cr_cblinfun_weak_star_def
      apply transfer
      by simp
  next
    case False
    then have False': ‹¬ summable_on_in weak_star_topology f A›
      apply transfer by simp
    then show ?thesis
      by (simp add: False infsum_not_exists not_summable_infsum_in_0 zero_cblinfun_weak_star.transfer)
  qed
qed
 *)

(* lemma map_filter_on_id: ‹map_filter_on S (λx. x) F = F› if ‹∀F x in F. x ∈ S›
  using that
  by (auto intro!: filter_eq_iff[THEN iffD2] simp: eventually_map_filter_on eventually_frequently_simps)
 *)

(* lemma inverse_real_inverse: ‹inverse_real_inst.inverse_real = inverse›
  by (simp add: HOL.nitpick_unfold)
 *)

(* lemma top_filter_parametric':
  assumes ‹Domainp r = S›
  assumes ‹right_total r›
  shows ‹rel_filter r (principal (Collect S)) ⊤›
proof (rule rel_filter.intros)
  define Z where ‹Z = principal {(x,y). r x y}›
  show ‹∀F (x, y) in Z. r x y›
    by (simp add: eventually_principal Z_def)
  show ‹map_filter_on {(x, y). r x y} fst Z = principal (Collect S)›
    using ‹Domainp r = S› by (auto simp add: filter_eq_iff eventually_principal Z_def eventually_map_filter_on)
  show ‹map_filter_on {(x, y). r x y} snd Z = ⊤›
    apply (auto simp add: filter_eq_iff eventually_principal Z_def eventually_map_filter_on)
    by (metis assms(2) right_totalE)
qed
 *)

(* 
lemma Inf_filter_parametric'[transfer_rule]:
  includes lifting_syntax
  fixes r :: ‹'rep ⇒ 'abs ⇒ bool›
  assumes ‹Domainp r = S›
  assumes [transfer_rule]: ‹bi_unique r› ‹right_total r›
  shows ‹(rel_set (rel_filter r) ===> rel_filter r)
     (λM. inf (Inf M) (principal (Collect S)))
     Inf›
proof (rule rel_funI)
  fix Fs Gs
  assume asm[transfer_rule]: ‹rel_set (rel_filter r) Fs Gs›
  show ‹rel_filter r (inf (Inf Fs) (principal (Collect S))) (Inf Gs)›
  proof (cases ‹Fs = {}›)
    case True
    then have ‹Gs = {}›
      by (metis asm empty_iff equals0I rel_setD2)
    show ?thesis
      using assms by (simp add: True ‹Gs = {}› top_filter_parametric')
  next
    case False
    then have ‹Gs ≠ {}›
      by (metis asm ex_in_conv rel_setD1)
    have dom: ‹Domainp (rel_filter r) F = (F ≤ principal (Collect S))› for F
      by (simp add: Domainp_rel_filter assms(1))
    have 1: ‹(rel_filter r)
       (Sup {F. Ball Fs ((≤) F) ∧ Domainp (rel_filter r) F})
       (Inf Gs)› (is ‹(rel_filter r) ?I _›)
      unfolding Inf_filter_def[abs_def]
      by transfer_prover
    have ‹F ≤ principal (Collect S)› if ‹F ∈ Fs› for F
      by (meson DomainPI asm dom rel_setD1 that)
    have ‹?I = (inf (Inf Fs) (principal (Collect S)))›
      unfolding dom Inf_filter_def
      apply auto
      apply (rule antisym)
      apply auto
        apply (simp add: Collect_mono_iff Sup_subset_mono)
      apply (metis (no_types, lifting) Sup_least mem_Collect_eq)
      by (smt (verit, del_insts) Collect_mono False Sup_subset_mono ‹⋀Fa. Fa ∈ Fs ⟹ Fa ≤ principal (Collect S)› bot.extremum_unique dual_order.refl inf.bounded_iff order_trans subsetI)
    show ?thesis
      using "1" ‹Sup {F. Ball Fs ((≤) F) ∧ Domainp (rel_filter r) F} = inf (Inf Fs) (principal (Collect S))› by fastforce
  qed
qed
 *)


(* 
lemma inf_filter_parametric'[transfer_rule]:
  includes lifting_syntax
  fixes r :: ‹'rep ⇒ 'abs ⇒ bool›
  assumes [transfer_rule]: ‹bi_unique r› ‹right_total r›
  shows ‹(rel_filter r ===> rel_filter r ===> rel_filter r)
     inf inf›
proof (rule rel_funI, rule rel_funI, rename_tac F1 F2 G1 G2)
  fix F1 F2 G1 G2
  assume asmF[transfer_rule]: ‹rel_filter r F1 F2›
  assume asmG[transfer_rule]: ‹rel_filter r G1 G2›
  then have *: ‹G1 ≤ principal (Collect (Domainp r))›
    by (meson Domainp.intros Domainp_rel_filter)
  have ‹rel_filter r (Inf {F1,G1} ⊓ principal (Collect (Domainp r))) (Inf {F2,G2})›
    by transfer_prover
  with * show ‹rel_filter r (inf F1 G1) (inf F2 G2)›
    by (auto simp: inf_assoc inf.absorb_iff1)
qed
 *)


(* lemma convergent_ow_typeclass2[simp]:
  assumes ‹open V›
  shows ‹convergent_ow V (openin (top_of_set V)) f ⟷ (∃l. f ⇢ l ∧ l ∈ V)›
  by (simp add: limitin_canonical_iff_gen assms)
 *)

(* lemma convergent_on_typeclass3[simp]:
  assumes ‹open V› ‹closed V› ‹range f ⊆ V›
  shows ‹convergent_ow V (openin (top_of_set V)) f ⟷ convergent f›
  apply (simp add: assms)
  by (meson assms(2) assms(3) closed_sequentially convergent_def range_subsetD)
 *)

(* lemma cmod_distrib_plus: ‹a ≥ 0 ⟹ b ≥ 0 ⟹ cmod (a + b) = cmod a + cmod b›
  by (simp add: cmod_Re) *)

lemma register_decomposition_converse:
  assumes unitary U
  shows register (λx. sandwich U (id_cblinfun o x))
  using _ unitary_sandwich_register apply (rule register_comp[unfolded o_def])
  using assms by auto

lemma iso_register_decomposition:
  assumes [simp]: iso_register F
  shows U. unitary U  F = sandwich U
proof -
  from register_decomposition
  have let 'c::type = register_decomposition_basis F in
        U. unitary U  F = sandwich U
  proof with_type_mp
    show [simp]: register F
      using assms iso_register_is_register by blast 
    with_type_case

(*     assume register_decomposition:
      ‹∃U :: ('a × 'c) ell2 ⇒CL 'b ell2. unitary U ∧ (∀θ. F θ = Complex_Bounded_Linear_Function.sandwich U (θ ⊗o id_cblinfun))›
 *)
    let ?ida = id_cblinfun :: 'c ell2 CL _

    from with_type_mp.premise
    obtain V :: ('a × 'c) ell2 CL 'b ell2 where unitary V
      and FV: F θ = sandwich V (θ o ?ida) for θ
      by auto

    have inj_V: inj ((*V) (sandwich V))
      by (meson unitary V register_inj unitary_sandwich_register)
    have surj F
      by (meson assms iso_register_inv_comp2 surj_iff)
    have surj_tensor: surj (λa::'a ell2 CL 'a ell2. a o ?ida)
      apply (rule surj_from_comp[where g=sandwich V])
      using surj F inj_V by (auto simp: FV)
    then obtain a :: 'a ell2 CL 'a ell2 
      where a: a o ?ida = butterfly (ket undefined) (ket undefined) o butterfly (ket undefined) (ket undefined)
      by (smt (verit, best) surjD)

    have selfbutter (ket (undefined, undefined))  0
      by (metis butterfly_apply cblinfun.zero_left complex_vector.scale_eq_0_iff ket_nonzero orthogonal_ket)
    with a have a  0
      by (auto simp: tensor_ell2_ket tensor_butterfly)


    obtain γ where γ: ?ida = γ *C butterfly (ket undefined) (ket undefined)
      apply atomize_elim
      using a a  0 by (rule tensor_op_almost_injective)
    then have ?ida (ket undefined) = γ *C (butterfly (ket undefined) (ket undefined) *V ket undefined)
      by (simp add: id_cblinfun = γ *C butterfly (ket undefined) (ket undefined) scaleC_cblinfun.rep_eq)
    then have ket undefined = γ *C ket undefined
      by (metis butterfly_apply cinner_ket_same id_cblinfun_apply ket_nonzero scaleC_cancel_right scaleC_one)
    then have γ = 1
      by (smt (z3) γ butterfly_apply butterfly_scaleC_left cblinfun_id_cblinfun_apply complex_vector.scale_cancel_right cinner_ket_same ket_nonzero)

    define T U where T = CBlinfun (λψ. ψ s ket undefined) and U = V oCL T
    have T: T ψ = ψ s ket undefined for ψ
      unfolding T_def
      apply (subst bounded_clinear_CBlinfun_apply)
      by (auto intro!: bounded_clinear_tensor_ell22)
    have sandwich T (butterfly (ket i) (ket j)) = butterfly (ket i) (ket j) o id_cblinfun for i j
      by (simp add: T sandwich_apply cblinfun_comp_butterfly butterfly_comp_cblinfun γ γ = 1 tensor_butterfly)
    then have sandwich_T: sandwich T a = a o ?ida for a
      apply (rule_tac fun_cong[where x=a])
      apply (rule weak_star_clinear_eq_butterfly_ketI[where T=weak_star_topology])
      by auto

    have F (butterfly x y) = V oCL (butterfly x y o ?ida) oCL V* for x y
      by (simp add: sandwich_apply FV)
    also have  x y = V oCL (butterfly (T x) (T y)) oCL V* for x y
      by (simp add: T γ γ = 1 tensor_butterfly)
    also have  x y = U oCL (butterfly x y) oCL U* for x y
      by (simp add: U_def butterfly_comp_cblinfun cblinfun_comp_butterfly)
    finally have F_rep:  F a = U oCL a oCL U* for a
      apply (rule_tac fun_cong[where x=a])
      apply (rule weak_star_clinear_eq_butterfly_ketI[where T=weak_star_topology])
      by (auto simp: clinear_register weak_star_cont_register simp flip: sandwich_apply)

    have isometry T
      apply (rule orthogonal_on_basis_is_isometry[where B=range ket])
      by (auto simp: T)
    moreover have T *S  = 
    proof -
      have 1: φ s ξ  range ((*V) T) for φ ξ
      proof -
        have T *V (cinner (ket undefined) ξ *C φ) = φ s (cinner (ket undefined) ξ *C ket undefined)
          by (simp add: T tensor_ell2_scaleC1 tensor_ell2_scaleC2)
        also have  = φ s (butterfly (ket undefined) (ket undefined) *V ξ)
          by simp
        also have  = φ s (?ida *V ξ)
          by (simp add: γ γ = 1)
        also have  = φ s ξ
          by simp
        finally show ?thesis
          by (metis range_eqI)
      qed

      have   ccspan {ket x | x. True}
        by (simp add: full_SetCompr_eq)
      also have   ccspan {φ s ξ | φ ξ. True}
        apply (rule ccspan_mono)
        by (auto simp flip: tensor_ell2_ket)
      also from 1 have   ccspan (range ((*V) T))
        by (auto intro!: ccspan_mono)
      also have  = T *S 
        by (metis (mono_tags, opaque_lifting) calculation cblinfun_image_ccspan cblinfun_image_mono eq_iff top_greatest)
      finally show T *S  = 
        using top.extremum_uniqueI by blast
    qed

    ultimately have unitary T
      by (rule surj_isometry_is_unitary)
    then have unitary U
      by (simp add: U_def unitary V)

    from F_rep unitary U show U. unitary U  F = sandwich U
      by (auto simp: sandwich_apply[abs_def])
  qed
  from this[THEN with_type_prepare_cancel, cancel_type_definition, OF with_type_nonempty, OF this]
  show ?thesis
    by -
qed

lemma complement_exists:
  fixes F :: 'a update  'b update
  assumes register F
  shows let 'c::type = register_decomposition_basis F in
         G :: 'c update  'b update. compatible F G  iso_register (F;G)
proof (use register_decomposition[OF register F] in rule with_type_mp)
  note [[simproc del: Laws_Quantum.compatibility_warn]]
  assume register_decomposition: U :: ('a × 'c) ell2 CL 'b ell2. unitary U  (θ. F θ = Complex_Bounded_Linear_Function.sandwich U (θ o id_cblinfun))
  then obtain U :: ('a × 'c) ell2 CL 'b ell2
    where [simp]: "unitary U" and F: F a = sandwich U (a o id_cblinfun) for a
    by auto
  define G :: 'c update  'b update where G b = sandwich U (id_cblinfun o b) for b
  have [simp]: register G
    unfolding G_def apply (rule register_decomposition_converse) by simp
  have F a oCL G b = G b oCL F a for a b
  proof -
    have F a oCL G b = sandwich U (a o b)
      by (auto simp: F G_def sandwich_apply cblinfun_assoc_right
          unitary U lift_cblinfun_comp[OF unitaryD1]
          lift_cblinfun_comp[OF comp_tensor_op])
    moreover have G b oCL F a = sandwich U (a o b)
      by (auto simp: F G_def sandwich_apply cblinfun_assoc_right
          unitary U lift_cblinfun_comp[OF unitaryD1]
          lift_cblinfun_comp[OF comp_tensor_op])
    ultimately show ?thesis by simp
  qed
  then have [simp]: compatible F G
    by (auto simp: compatible_def register F)
  moreover have iso_register (F;G)
  proof -
    have (F;G) (a o b) = sandwich U (a o b) for a b
      by (auto simp: register_pair_apply F G_def sandwich_apply cblinfun_assoc_right
          unitary U lift_cblinfun_comp[OF unitaryD1]
          lift_cblinfun_comp[OF comp_tensor_op])
    then have FG: (F;G) = sandwich U
      apply (rule tensor_extensionality[rotated -1])
      by (simp_all add: unitary_sandwich_register)
    define I where I = sandwich (U*) for x
    have [simp]: register I
      by (simp add: I_def unitary_sandwich_register)
    have I o (F;G) = id and FGI: (F;G) o I = id
      by (auto intro!:ext simp: I_def[abs_def] FG sandwich_apply cblinfun_assoc_right
          unitary U lift_cblinfun_comp[OF unitaryD1] lift_cblinfun_comp[OF unitaryD2]
          lift_cblinfun_comp[OF comp_tensor_op])
    then show iso_register (F;G)
      by (auto intro!: iso_registerI)
  qed
  ultimately show G :: 'c update  'b update. compatible F G  iso_register (F;G)
    apply (rule_tac exI[of _ G]) by auto
qed

lemma commutant_exchange:
  fixes F :: 'a update  'b update
  assumes iso_register F
  shows commutant (F ` X) = F ` commutant X
proof (rule Set.set_eqI)
  fix x :: 'b update
  from assms
  obtain G where F o G = id and G o F = id and [simp]: register G
    using iso_register_def by blast
  from assms have [simp]: register F
    using iso_register_def by blast
  have x  commutant (F ` X)  (y  F ` X. x oCL y = y oCL x)
    by (simp add: commutant_def)
  also have   (y  F ` X. G x oCL G y = G y oCL G x)
    by (metis (no_types, opaque_lifting) F  G = id G o F = id register G comp_def eq_id_iff register_def)
  also have   (y  X. G x oCL y = y oCL G x)
    by (simp add: G  F = id pointfree_idE)
  also have   G x  commutant X
    by (simp add: commutant_def)
  also have   x  F ` commutant X
    by (metis (no_types, opaque_lifting) G  F = id F  G = id image_iff pointfree_idE)
  finally show x  commutant (F ` X)  x  F ` commutant X
    by -
qed

lemma complement_range:
  assumes [simp]: compatible F G and [simp]: iso_register (F;G)
  shows range G = commutant (range F)
proof -
  have [simp]: register F register G
    using assms compatible_def by metis+
  have [simp]: (F;G) (a o b) = F a oCL G b for a b
    using Laws_Quantum.register_pair_apply assms by blast
  have [simp]: range F = (F;G) ` range (λa. a o id_cblinfun)
    by force
  have [simp]: range G = (F;G) ` range (λb. id_cblinfun o b)
    by force
  show range G = commutant (range F)
    by (simp add: commutant_exchange commutant_tensor1)
qed

(* lemma isCont_iff_preserves_convergence:
  assumes ‹⋀F. (id ⤏ a) F ⟹ (f ⤏ f a) F›
  shows ‹isCont f a›
  using assms
  by (simp add: isCont_def Lim_at_id) *)


lemma register_inv_G_o_F: 
  assumes [simp]: register F and [simp]: register G and range_FG: range F  range G
  shows register (inv G  F)
proof -
  note [[simproc del: Laws_Quantum.compatibility_warn]]
  define GF where GF = inv G o F
  have F_rangeG[simp]: F x  range G for x
   using range_FG by auto
  have [simp]: inj F and [simp]: inj G
    by (simp_all add: register_inj)
  have [simp]: bounded_clinear F bounded_clinear G
    by (simp_all add: register_bounded_clinear)
  have [simp]: clinear F clinear G
    by (simp_all add: bounded_clinear.clinear)
  have addJ: GF (x + y) = GF x + GF y for x y
    unfolding GF_def o_def
    apply (rule injD[OF inj G])
    apply (subst complex_vector.linear_add[OF clinear G])
    apply (subst Hilbert_Choice.f_inv_into_f[where f=G], simp)+
    by (simp add: complex_vector.linear_add)
  have scaleJ: GF (r *C x) = r *C GF x for r x
    unfolding GF_def o_def
    apply (rule injD[OF inj G])
    apply (subst complex_vector.linear_scale[OF clinear G])
    apply (subst Hilbert_Choice.f_inv_into_f[where f=G], simp)+
    by (simp add: complex_vector.linear_scale)
  have unitalJ: GF id_cblinfun = id_cblinfun
    unfolding GF_def o_def
    apply (rule injD[OF inj G])
    apply (subst Hilbert_Choice.f_inv_into_f[where f=G])
    by (auto intro!: range_eqI[of _ _ id_cblinfun]) 
  have multJ: GF (a oCL b) = GF a oCL GF b for a b
    unfolding GF_def o_def
    apply (rule injD[OF inj G])
    apply (subst register_mult[symmetric, OF register G])
    apply (subst Hilbert_Choice.f_inv_into_f[where f=G], simp)+
    by (simp add: register_mult)
  have adjJ: GF (a*) = (GF a)* for a
    unfolding GF_def o_def
    apply (rule injD[OF inj G])
    apply (subst register_adjoint[OF register G])
    apply (subst Hilbert_Choice.f_inv_into_f[where f=G], simp)+
    using register F register_adjoint by blast
  have normJ: norm (GF a) = norm a for a
    unfolding GF_def
    by (metis F_rangeG register F register G f_inv_into_f o_def register_norm)
  have weak_star_J: continuous_map weak_star_topology weak_star_topology GF
  proof -
    have continuous_map weak_star_topology weak_star_topology F
      by (simp add: weak_star_cont_register)
    then have continuous_map weak_star_topology (subtopology weak_star_topology (range F)) F
      by (simp add: continuous_map_into_subtopology)
    moreover have continuous_map (subtopology weak_star_topology (range G)) weak_star_topology (inv G)
      using register G register_inv_weak_star_continuous by blast
    ultimately show continuous_map weak_star_topology weak_star_topology GF
      by (simp add: GF_def range_FG continuous_map_compose continuous_map_from_subtopology_mono)
  qed

  from addJ scaleJ unitalJ multJ adjJ normJ weak_star_J
  show register GF
    unfolding register_def by (auto intro!: bounded_clinearI[where K=1])
qed


lemma same_range_equivalent:
  fixes F :: 'a update  'c update and G :: 'b update  'c update
  assumes [simp]: register F and [simp]: register G
  assumes range_FG: range F = range G
  shows equivalent_registers F G
proof -
  note [[simproc del: Laws_Quantum.compatibility_warn]]
  have regGF: register (inv G o F)
    using assms by (auto intro!: register_inv_G_o_F)
  have regFG: register (inv F o G)
    using assms by (auto intro!: register_inv_G_o_F)
  have inj G
    by (simp add: register_inj)
  with range_FG have GFFG: (inv G o F) o (inv F o G) = id
    by (smt (verit) assms(1) f_inv_into_f invI isomorphism_expand o_def rangeI register_inj)
  have inj F
    by (simp add: register_inj)
  with range_FG have FGGF: (inv F o G) o (inv G o F) = id
    by (metis GFFG fun.set_map image_inv_f_f left_right_inverse_eq surj_iff)
  from regGF regFG GFFG FGGF have iso_FG: iso_register (inv F o G)
    using iso_registerI by auto
  have FFG: F o (inv F o G) = G
    by (smt (verit) FGGF GFFG fun.inj_map_strong fun.map_comp fun.set_map image_inv_f_f inj_on_imageI2 inv_id inv_into_injective inv_o_cancel o_inv_o_cancel range_FG surj_id surj_imp_inj_inv)
  from FFG iso_FG show equivalent_registers F G
    by (simp add: equivalent_registersI)
qed

lemma complement_unique:
  assumes "compatible F G" and iso_register (F;G)
  assumes "compatible F H" and iso_register (F;H)
  shows equivalent_registers G H
  by (metis assms compatible_def complement_range same_range_equivalent)

















subsection ‹Finite dimensional complement›

typedef ('a, 'b::finite) complement_domain_simple = {..< if CARD('b) div CARD('a)  0 then CARD('b) div CARD('a) else 1}
  by auto

instance complement_domain_simple :: (type, finite) finite
proof intro_classes
  have inj Rep_complement_domain_simple
    by (simp add: Rep_complement_domain_simple_inject inj_on_def)
  moreover have finite (range Rep_complement_domain_simple)
    by (metis finite_lessThan type_definition.Rep_range type_definition_complement_domain_simple)
  ultimately show finite (UNIV :: ('a,'b) complement_domain_simple set)
    using finite_image_iff by blast
qed

lemma CARD_complement_domain: 
  assumes CARD('b::finite) = n * CARD('a)
  shows CARD(('a,'b) complement_domain_simple) = n
proof -
  from assms have n > 0
    by (metis nat_0_less_mult_iff zero_less_card_finite)
  have *: inj Rep_complement_domain_simple
    by (simp add: Rep_complement_domain_simple_inject inj_on_def)
  moreover have card (range (Rep_complement_domain_simple :: ('a,'b) complement_domain_simple  _)) = n
    apply (subst type_definition.Rep_range[OF type_definition_complement_domain_simple])
    using assms n > 0 apply simp
    by force
  ultimately show ?thesis
    by (metis card_image)
qed



lemma register_decomposition_finite_aux:
  fixes Φ :: 'a::finite update  'b::finite update
  assumes [simp]: register Φ
  shows U :: ('a × ('a, 'b) complement_domain_simple) ell2 CL 'b ell2. unitary U  
              (θ. Φ θ = sandwich U (θ o id_cblinfun))
  ― ‹Proof based on citedaws21unitalanswer
proof -
  note [[simproc del: compatibility_warn]]
  fix ξ0 :: 'a

  have [simp]: clinear Φ
    by (simp add: clinear_register)

  define P where P i = Proj (ccspan {ket i}) for i :: 'a
  have P_butter: P i = butterfly (ket i) (ket i) for i
    by (simp add: P_def butterfly_eq_proj)

  define P' where P' i = Φ (P i) for i :: 'a
  have proj_P': is_Proj (P' i) for i
    by (simp add: P_def P'_def register_projector)
  have (iUNIV. P i) = id_cblinfun
    using sum_butterfly_ket P_butter by simp
  then have sumP'id: (iUNIV. P' i) = id_cblinfun
    unfolding P'_def 
    apply (subst complex_vector.linear_sum[OF clinear Φ, symmetric])
    by auto

  define S where S i = P' i *S  for i :: 'a
  have P'id: P' i *V ψ = ψ if ψ  space_as_set (S i) for i ψ
    using S_def that proj_P'
    by (metis cblinfun_fixes_range is_Proj_algebraic)

  obtain B0 where finiteB0: finite (B0 i) and cspanB0: cspan (B0 i) = space_as_set (S i) for i
    apply atomize_elim apply (simp flip: all_conj_distrib) apply (rule choice)
    by (meson cfinite_dim_finite_subspace_basis csubspace_space_as_set)

  obtain B where orthoB: is_ortho_set (B i)
    and normalB: b. b  B i  norm b = 1
    and cspanB: cspan (B i) = cspan (B0 i)
    and finiteB: finite (B i) for i
    apply atomize_elim apply (simp flip: all_conj_distrib) apply (rule choice)
    using orthonormal_basis_of_cspan[OF finiteB0] by blast

  from cspanB cspanB0 have cspanB: cspan (B i) = space_as_set (S i) for i
    by simp
  then have ccspanB: ccspan (B i) = S i for i
    by (metis ccspan.rep_eq closure_finite_cspan finiteB space_as_set_inject)
  from orthoB have indepB: cindependent (B i) for i
    by (simp add: Complex_Inner_Product.is_ortho_set_cindependent)

  have orthoBiBj: is_orthogonal x y if x  B i and y  B j and i  j for x y i j
  proof -
    from x  B i obtain x' where x: x = P' i *V x'
      by (metis S_def cblinfun_fixes_range complex_vector.span_base cspanB is_Proj_idempotent proj_P')
    from y  B j obtain y' where y: y = P' j *V y'
      by (metis S_def cblinfun_fixes_range complex_vector.span_base cspanB is_Proj_idempotent proj_P')
    have cinner x y = cinner (P' i *V x') (P' j *V  y')
      using x y by simp
    also have  = cinner (P' j *V P' i *V x') y'
      by (metis cinner_adj_left is_Proj_algebraic proj_P')
    also have  = cinner (Φ (P j oCL P i) *V x') y'
      unfolding P'_def register_mult[OF register Φ, symmetric] by simp
    also have  = cinner (Φ (butterfly (ket j) (ket j) oCL butterfly (ket i) (ket i)) *V x') y'
      unfolding P_butter by simp
    also have  = cinner (Φ 0 *V x') y'
      by (metis butterfly_comp_butterfly complex_vector.scale_eq_0_iff orthogonal_ket that(3))
    also have  = 0
      by (simp add: complex_vector.linear_0)
    finally show ?thesis
      by -
  qed


  define B' where B' = (iUNIV. B i)

  have P'B: P' i = Proj (ccspan (B i)) for i
    unfolding ccspanB S_def
    using proj_P' Proj_on_own_range'[symmetric] is_Proj_algebraic by blast

  have (iUNIV. P' i) = Proj (ccspan B')
  proof (unfold B'_def, use finite[of UNIV] in induction)
    case empty
    show ?case by auto
  next
    case (insert j M)
    have (iinsert j M. P' i) = P' j + (iM. P' i)
      by (meson insert.hyps(1) insert.hyps(2) sum.insert)
    also have  = Proj (ccspan (B j)) + Proj (ccspan (iM. B i))
      unfolding P'B insert.IH[symmetric] by simp
    also have  = Proj (ccspan (B j  (iM. B i)))
      apply (rule Proj_orthog_ccspan_union[symmetric])
      using orthoBiBj insert.hyps(2) by auto
    also have  = Proj (ccspan (iinsert j M. B i))
      by auto
    finally show ?case
      by simp
  qed

  with sumP'id 
  have ccspanB': ccspan B' = 
    by (metis Proj_range cblinfun_image_id)
  hence cspanB': cspan B' = UNIV
    by (metis B'_def finiteB ccspan.rep_eq finite_UN_I finite_class.finite_UNIV closure_finite_cspan top_ccsubspace.rep_eq)

  from orthoBiBj orthoB have orthoB': is_ortho_set B'
    unfolding B'_def is_ortho_set_def by blast
  then have indepB': cindependent B'
    using is_ortho_set_cindependent by blast
  have cardB': card B' = CARD('b)
    apply (subst complex_vector.dim_span_eq_card_independent[symmetric])
     apply (rule indepB')
    apply (subst cspanB')
    using cdim_UNIV_ell2 by auto

  from orthoBiBj orthoB
  have Bdisj: B i  B j = {} if i  j for i j
    unfolding is_ortho_set_def
    using that by fastforce

  have cardBsame: card (B i) = card (B j) for i j
  proof -
    define Si_to_Sj where Si_to_Sj i j ψ = Φ (butterfly (ket j) (ket i)) *V ψ for i j ψ
    have S2S2S: Si_to_Sj j i (Si_to_Sj i j ψ) = ψ if ψ  space_as_set (S i) for i j ψ
      using that P'id
      by (simp add: Si_to_Sj_def cblinfun_apply_cblinfun_compose[symmetric] register_mult P_butter P'_def)
    also have lin[simp]: clinear (Si_to_Sj i j) for i j
      unfolding Si_to_Sj_def by simp
    have S2S: Si_to_Sj i j x  space_as_set (S j) for i j x
    proof -
      have Si_to_Sj i j x = P' j *V Si_to_Sj i j x
        by (simp add: Si_to_Sj_def cblinfun_apply_cblinfun_compose[symmetric] register_mult P_butter P'_def)
      also have P' j *V Si_to_Sj i j x  space_as_set (S j)
        by (simp add: S_def)
      finally show ?thesis by -
    qed
    have bij: bij_betw (Si_to_Sj i j) (space_as_set (S i)) (space_as_set (S j))
      apply (rule bij_betwI[where g=Si_to_Sj j i])
      using S2S S2S2S by (auto intro!: funcsetI)
    have cdim (space_as_set (S i)) = cdim (space_as_set (S j))
      using lin apply (rule isomorphic_equal_cdim[where f=Si_to_Sj i j])
      using bij by (auto simp: bij_betw_def)
    then show ?thesis
      by (metis complex_vector.dim_span_eq_card_independent cspanB indepB)
  qed

  have CARD'b: CARD('b) = card (B ξ0) * CARD('a)
  proof -
    have CARD('b) = card B'
      using cardB' by simp
    also have  = (iUNIV. card (B i))
      unfolding B'_def apply (rule card_UN_disjoint)
      using finiteB Bdisj by auto
    also have  = ((i::'a)UNIV. card (B ξ0))
      using cardBsame by metis
    also have  = card (B ξ0) * CARD('a)
      by auto
    finally show ?thesis by -
  qed

  obtain f where bij_f: bij_betw f (UNIV::('a,'b) complement_domain_simple set) (B ξ0)
    apply atomize_elim apply (rule finite_same_card_bij)
    using finiteB CARD_complement_domain[OF CARD'b] by auto

  define u where u = (λ(ξ,α). Φ (butterfly (ket ξ) (ket ξ0)) *V f α) for ξ :: 'a and α :: ('a,'b) complement_domain_simple
  obtain U where Uapply: U *V ket ξα = u ξα for ξα
    apply atomize_elim
    apply (rule exI[of _ cblinfun_extension (range ket) (λk. u (inv ket k))])
    apply (subst cblinfun_extension_apply)
      apply (rule cblinfun_extension_exists_finite_dim)
    by (auto simp add: inj_ket cindependent_ket)

  define eqa where eqa a b = (if a = b then 1 else 0 :: complex) for a b :: 'a
  define eqc where eqc a b = (if a = b then 1 else 0 :: complex) for a b :: ('a,'b) complement_domain_simple
  define eqac where eqac a b = (if a = b then 1 else 0 :: complex) for a b :: 'a * ('a,'b) complement_domain_simple

  have cinner (U *V ket ξα) (U *V ket ξ'α') = eqac ξα ξ'α' for ξα ξ'α'
  proof -
    obtain ξ α ξ' α' where ξα: ξα = (ξ,α) and ξ'α': ξ'α' = (ξ',α')
      apply atomize_elim by auto
    have cinner (U *V ket (ξ,α)) (U *V ket (ξ', α')) = cinner (Φ (butterfly (ket ξ) (ket ξ0)) *V f α) (Φ (butterfly (ket ξ') (ket ξ0)) *V f α')
      unfolding Uapply u_def by simp
    also have  = cinner ((Φ (butterfly (ket ξ') (ket ξ0)))* *V Φ (butterfly (ket ξ) (ket ξ0)) *V f α) (f α')
      by (simp add: cinner_adj_left)
    also have  = cinner (Φ (butterfly (ket ξ') (ket ξ0) *) *V Φ (butterfly (ket ξ) (ket ξ0)) *V f α) (f α')
      by (metis (no_types, lifting) assms register_def)
    also have  = cinner (Φ (butterfly (ket ξ0) (ket ξ') oCL butterfly (ket ξ) (ket ξ0)) *V f α) (f α')
      by (simp add: register_mult cblinfun_apply_cblinfun_compose[symmetric])
    also have  = cinner (Φ (eqa ξ' ξ *C butterfly (ket ξ0) (ket ξ0)) *V f α) (f α')
      by (simp add: eqa_def cinner_ket) 
    also have  = eqa ξ' ξ * cinner (Φ (butterfly (ket ξ0) (ket ξ0)) *V f α) (f α')
      by (smt (verit, ccfv_threshold) clinear Φ eqa_def cblinfun.scaleC_left cinner_commute 
              cinner_scaleC_left cinner_zero_right complex_cnj_one complex_vector.linear_scale)
    also have  = eqa ξ' ξ * cinner (P' ξ0 *V f α) (f α')
      using P_butter P'_def by simp
    also have  = eqa ξ' ξ * cinner (f α) (f α')
      apply (subst P'id)
       apply (metis bij_betw_imp_surj_on bij_f complex_vector.span_base cspanB rangeI)
      by simp
    also have  = eqa ξ' ξ * eqc α α'
    proof -
      from normalB bij_f
      have aux1: f α' C f α'  1  eqa ξ' ξ = 0
        by (metis bij_betw_imp_surj_on cnorm_eq_1 rangeI)
      from orthoB bij_f have aux2: α  α'  f α C f α'  0  eqa ξ' ξ = 0
        by (smt (z3) bij_betw_iff_bijections iso_tuple_UNIV_I is_ortho_set_def)
      from aux1 aux2 show ?thesis
        unfolding  eqc_def by auto
    qed
    finally show ?thesis
      by (simp add: eqa_def eqac_def eqc_def ξ'α' ξα)
  qed
  then have [simp]: isometry U
    apply (rule_tac orthogonal_on_basis_is_isometry[where B=range ket])
    using eqac_def by auto

  have sandwich (U*) (Φ (butterfly (ket ξ) (ket η))) = butterfly (ket ξ) (ket η) o id_cblinfun for ξ η
  proof (rule equal_ket, rename_tac ξ1α)
    fix ξ1α obtain ξ1 :: 'a and α :: ('a,'b) complement_domain_simple where ξ1α: ξ1α = (ξ1,α) 
      apply atomize_elim by auto
    have sandwich (U*) (Φ (butterfly (ket ξ) (ket η))) *V ket ξ1α = U* *V Φ (butterfly (ket ξ) (ket η)) *V Φ (butterfly (ket ξ1) (ket ξ0)) *V f α
      by (simp add: sandwich_apply[abs_def] cblinfun_apply_cblinfun_compose ξ1α Uapply u_def)
    also have  = U* *V Φ (butterfly (ket ξ) (ket η) oCL butterfly (ket ξ1) (ket ξ0)) *V f α
      by (metis (no_types, lifting) assms butterfly_comp_butterfly lift_cblinfun_comp(4) register_mult)
    also have  = U* *V Φ (eqa η ξ1 *C butterfly (ket ξ) (ket ξ0)) *V f α
      by (simp add: eqa_def cinner_ket)
    also have  = eqa η ξ1 *C U* *V Φ (butterfly (ket ξ) (ket ξ0)) *V f α
      by (simp add: complex_vector.linear_scale)
    also have  = eqa η ξ1 *C U* *V U *V ket (ξ, α)
      unfolding Uapply u_def by simp
    also from isometry U have  = eqa η ξ1 *C ket (ξ, α)
      unfolding cblinfun_apply_cblinfun_compose[symmetric] by simp
    also have  = (butterfly (ket ξ) (ket η) *V ket ξ1) s ket α
      by (simp add: eqa_def tensor_ell2_scaleC1 tensor_ell2_ket)
    also have  = (butterfly (ket ξ) (ket η) o id_cblinfun) *V ket ξ1α
      by (simp add: ξ1α tensor_op_ket)
    finally show sandwich (U*) (Φ (butterfly (ket ξ) (ket η))) *V ket ξ1α = (butterfly (ket ξ) (ket η) o id_cblinfun) *V ket ξ1α
      by -
  qed
  then have 1: sandwich (U*) (Φ θ) = θ o id_cblinfun for θ
    apply (rule clinear_eq_butterfly_ketI[THEN fun_cong, where x=θ, rotated -1])
    by (auto intro: bounded_clinear.clinear bounded_linear_intros register_bounded_clinear register Φ)

  have [simp]: unitary U
  proof -
    have Φ (butterfly (ket ξ) (ket ξ1)) *S   U *S  for ξ ξ1
    proof -
      have *: Φ (butterfly (ket ξ) (ket ξ0)) *V b  space_as_set (U *S ) if b  B ξ0 for b
        apply (subst asm_rl[of Φ (butterfly (ket ξ) (ket ξ0)) *V b = u (ξ, inv f b)])
         apply (simp add: u_def, metis bij_betw_inv_into_right bij_f that)
        by (metis Uapply cblinfun_apply_in_image)

      have Φ (butterfly (ket ξ) (ket ξ1)) *S  = Φ (butterfly (ket ξ) (ket ξ0)) *S Φ (butterfly (ket ξ0) (ket ξ0)) *S Φ (butterfly (ket ξ0) (ket ξ1)) *S 
        unfolding cblinfun_compose_image[symmetric] register_mult[OF assms]
        by simp
      also have   Φ (butterfly (ket ξ) (ket ξ0)) *S Φ (butterfly (ket ξ0) (ket ξ0)) *S 
        by (meson cblinfun_image_mono top_greatest)
      also have  = Φ (butterfly (ket ξ) (ket ξ0)) *S S ξ0
        by (simp add: S_def P'_def P_butter)
      also have  = Φ (butterfly (ket ξ) (ket ξ0)) *S ccspan (B ξ0)
        by (simp add: ccspanB)
      also have  = ccspan (Φ (butterfly (ket ξ) (ket ξ0)) ` B ξ0)
        by (meson cblinfun_image_ccspan)
      also have   U *S 
        by (rule ccspan_leqI, use * in auto)
      finally show ?thesis by -
    qed
    moreover have Φ id_cblinfun *S   (SUP ξUNIV. Φ (butterfly (ket ξ) (ket ξ)) *S )
      unfolding sum_butterfly_ket[symmetric]
      apply (subst complex_vector.linear_sum, simp)
      by (rule cblinfun_sum_image_distr)
    ultimately have Φ id_cblinfun *S   U *S 
      by (metis (no_types, lifting) Proj_range Proj_top SUP_le_iff assms register_of_id top_le)
    then have U *S  = 
      using top.extremum_unique by auto
    with isometry U show unitary U
      by (rule surj_isometry_is_unitary)
  qed

  have Φ θ = sandwich U (θ o id_cblinfun) for θ
  proof -
    from 1 have sandwich U (sandwich (U*) *V Φ θ) = sandwich U (θ o id_cblinfun)
      by simp
    then show ?thesis
      by (simp flip: sandwich_compose cblinfun_apply_cblinfun_compose)
  qed

  then show ?thesis
    apply (rule_tac exI[of _ U])
    by simp
qed

lemma register_decomposition_finite:
  fixes Φ :: 'a update  'b::finite update
  assumes [simp]: register Φ
  shows U :: ('a × ('a, 'b) complement_domain_simple) ell2 CL 'b ell2. unitary U  
              (θ. Φ θ = sandwich U (θ o id_cblinfun))
proof -
  have inj_butterket: inj (λa. butterfly (ket a) (ket a) :: 'a update)
  proof (rule injI)
    fix x y :: 'a
    assume butterfly (ket x) (ket x) = butterfly (ket y) (ket y)
    then have butterfly (ket x) (ket x) *V ket x = butterfly (ket y) (ket y)  *V ket x
      by simp
    then show x = y
      apply (cases x = y)
      by (auto simp: cinner_ket)
  qed

  from cindependent_butterfly_ket
  have cindependent (range (λa. butterfly (ket a) (ket a)) :: 'a update set)
    apply (subgoal_tac (range (λa. butterfly (ket a) (ket a)) :: 'a update set)  {butterfly (ket i) (ket j) |i j. True})
    by (auto intro: complex_vector.dependent_mono)
  then have cindependent (Φ ` range (λa. butterfly (ket a) (ket a)))
    apply (rule complex_vector.linear_independent_injective_image[rotated])
    by (simp_all add: register_inj clinear_register)
  then have finite (Φ ` range (λa. butterfly (ket a) (ket a)))
    using cindependent_cfinite_dim_finite by blast
  then have finite (range (λa. butterfly (ket a) (ket a)) :: 'a update set)
    apply (rule Finite_Set.inj_on_finite[rotated -1, where f=Φ])
    by (simp_all add: register_inj)
  then have finite (UNIV :: 'a set)
    apply (rule Finite_Set.inj_on_finite[rotated -1, where f=λa. butterfly (ket a) (ket a)])
     apply (rule inj_butterket)
    by simp
  then have class.finite TYPE('a)
    by intro_classes
  from register_decomposition_finite_aux[unoverload_type 'a, OF this]
  show ?thesis
    using assms by metis
qed

hide_fact register_decomposition_finite_aux

lemma complement_exists_simple:
  fixes F :: 'a update  'b::finite update
  assumes register F
  shows G :: ('a, 'b) complement_domain_simple update  'b update. compatible F G  iso_register (F;G)
proof -
  note [[simproc del: Laws_Quantum.compatibility_warn]]
  obtain U :: ('a × ('a, 'b) complement_domain_simple) ell2 CL 'b ell2
    where [simp]: "unitary U" and F: F a = sandwich U (a o id_cblinfun) for a
    apply atomize_elim using assms by (rule register_decomposition_finite)
  define G :: (('a, 'b) complement_domain_simple) update  'b update where G b = sandwich U (id_cblinfun o b) for b
  have [simp]: register G
    unfolding G_def apply (rule register_decomposition_converse) by simp
  have F a oCL G b = G b oCL F a for a b
  proof -
    have F a oCL G b = sandwich U (a o b)
      by (auto simp: F G_def sandwich_apply cblinfun_assoc_left lift_cblinfun_comp[OF unitaryD1] 
          lift_cblinfun_comp[OF comp_tensor_op] unitary U)
    moreover have G b oCL F a = sandwich U (a o b)
      by (auto simp: F G_def sandwich_apply cblinfun_assoc_left lift_cblinfun_comp[OF unitaryD1] 
          lift_cblinfun_comp[OF comp_tensor_op] unitary U)
    ultimately show ?thesis by simp
  qed
  then have [simp]: compatible F G
    by (auto simp: compatible_def register F register G)
  moreover have iso_register (F;G)
  proof -
    have (F;G) (a o b) = sandwich U (a o b) for a b
      by (auto simp: register_pair_apply F G_def sandwich_apply cblinfun_assoc_left lift_cblinfun_comp[OF unitaryD1] 
          lift_cblinfun_comp[OF comp_tensor_op] unitary U)
    then have FG: (F;G) = sandwich U
      apply (rule tensor_extensionality[rotated -1])
      by (simp_all add: bounded_cbilinear.add_left bounded_cbilinear_cblinfun_compose bounded_cbilinear.add_right clinearI unitary_sandwich_register)

    define I where I = sandwich (U*) for x
    have [simp]: register I
      by (simp add: I_def unitary_sandwich_register)
    have IFG: I o (F;G) = id
      by (auto intro!:ext simp: I_def[abs_def] FG sandwich_apply lift_cblinfun_comp[OF unitaryD1]
          unitary U cblinfun_assoc_left)
    have FGI: (F;G) o I = id
      by (auto intro!:ext simp: I_def[abs_def] FG sandwich_apply cblinfun_assoc_left
          lift_cblinfun_comp[OF unitaryD1] lift_cblinfun_comp[OF unitaryD2] unitary U)
    from IFG FGI show iso_register (F;G)
      by (auto intro!: iso_registerI)
  qed
  ultimately show ?thesis
    apply (rule_tac exI[of _ G]) by (auto)
qed

unbundle no cblinfun_syntax
unbundle no lattice_syntax
unbundle no register_syntax

end