Theory Pure_States
theory Pure_States
  imports Quantum_Extra2 "HOL-Eisbach.Eisbach"
begin
unbundle cblinfun_syntax
unbundle register_syntax
definition ‹pure_state_target_vector F η⇩F = (if ket default ∈ range (cblinfun_apply (F (butterfly η⇩F η⇩F)))
   then ket default
   else (SOME η'. norm η' = 1 ∧ η' ∈ range (cblinfun_apply (F (butterfly η⇩F η⇩F)))))›
lemma pure_state_target_vector_eqI:
  assumes ‹F (butterfly η⇩F η⇩F) = G (butterfly η⇩G η⇩G)›
  shows ‹pure_state_target_vector F η⇩F = pure_state_target_vector G η⇩G›
  by (simp add: assms pure_state_target_vector_def)
lemma pure_state_target_vector_ket_default: ‹pure_state_target_vector F η⇩F = ket default› if ‹ket default ∈ range (cblinfun_apply (F (butterfly η⇩F η⇩F)))›
  by (simp add: pure_state_target_vector_def that)
lemma
  assumes [simp]: ‹η⇩F ≠ 0› ‹register F›
  shows pure_state_target_vector_in_range: ‹pure_state_target_vector F η⇩F ∈ range ((*⇩V) (F (selfbutter η⇩F)))› (is ?range)
    and pure_state_target_vector_norm: ‹norm (pure_state_target_vector F η⇩F) = 1› (is ?norm)
proof -
  from assms have ‹selfbutter η⇩F ≠ 0›
    by (metis butterfly_0_right complex_vector.scale_zero_right inj_selfbutter_upto_phase)
  then have ‹F (selfbutter η⇩F) ≠ 0›
    using register_inj[OF ‹register F›, THEN injD, where y=0]
    by (auto simp: complex_vector.linear_0 clinear_register)
  then obtain ψ' where ψ': ‹F (selfbutter η⇩F) *⇩V ψ' ≠ 0›
    by (meson cblinfun_eq_0_on_UNIV_span complex_vector.span_UNIV)
  have ex: ‹∃ψ. norm ψ = 1 ∧ ψ ∈ range ((*⇩V) (F (selfbutter η⇩F)))›
    apply (rule exI[of _ ‹(F (selfbutter η⇩F) *⇩V ψ') /⇩C norm (F (selfbutter η⇩F) *⇩V ψ')›])
    using ψ' 
    apply (simp add: norm_inverse cblinfun.scaleC_right)
    by (simp flip: cblinfun.scaleC_right)
  then show ?range
    by (metis (mono_tags, lifting) pure_state_target_vector_def verit_sko_ex')
  show ?norm
    apply (simp add: pure_state_target_vector_def)
    using ex by (metis (mono_tags, lifting) verit_sko_ex')
qed
lemma pure_state_target_vector_correct: 
  assumes [simp]: ‹η ≠ 0› ‹register F›
  shows ‹F (selfbutter η) *⇩V pure_state_target_vector F η ≠ 0›
proof -
  obtain ψ where ψ: ‹F (selfbutter η) ψ = pure_state_target_vector F η›
    apply atomize_elim
    using pure_state_target_vector_in_range[OF assms]
    by (smt (verit, best) image_iff top_ccsubspace.rep_eq top_set_def)
  define n where ‹n = cinner η η›
  then have ‹n ≠ 0›
    by auto
  have pure_state_target_vector_neq0: ‹pure_state_target_vector F η ≠ 0›
    using pure_state_target_vector_norm[OF assms]
    by auto
  have ‹F (selfbutter η) *⇩V pure_state_target_vector F η = F (selfbutter η) *⇩V F (selfbutter η) *⇩V ψ›
    by (simp add: ψ)
  also have ‹… = n *⇩C F (selfbutter η) *⇩V ψ›
    by (simp flip: cblinfun_apply_cblinfun_compose add: register_mult register_scaleC n_def)
  also have ‹… = n *⇩C pure_state_target_vector F η›
    by (simp add: ψ)
  also have ‹… ≠ 0›
    using pure_state_target_vector_neq0 ‹n ≠ 0›
    by auto
  finally show ?thesis
    by -
qed
definition ‹pure_state' F ψ η⇩F = F (butterfly ψ η⇩F) *⇩V pure_state_target_vector F η⇩F›
abbreviation ‹pure_state F ψ ≡ pure_state' F ψ (ket default)›
nonterminal pure_tensor
syntax "_pure_tensor" :: ‹'a ⇒ 'b ⇒ pure_tensor ⇒ pure_tensor› ("_ _ ⊗⇩p _" [1000, 0, 0] 1000)
syntax "_pure_tensor2" :: ‹'a ⇒ 'b ⇒ 'c ⇒ 'd ⇒ pure_tensor› ("_ _ ⊗⇩p _ _" [1000, 0, 1000, 0] 1000)
syntax "_pure_tensor1" :: ‹'a ⇒ 'b ⇒ pure_tensor›
syntax "_pure_tensor_start" :: ‹pure_tensor ⇒ 'a› ("'(_')")
translations
  "_pure_tensor2 F ψ G φ" ⇀ "CONST pure_state (F; G) (ψ ⊗⇩s φ)"
  "_pure_tensor F ψ (CONST pure_state G φ)" ⇀ "CONST pure_state (F; G) (ψ ⊗⇩s φ)"
  "_pure_tensor_start x" ⇀ "x"
  "_pure_tensor_start (_pure_tensor2 F ψ G φ)" ↽ "CONST pure_state (F; G) (ψ ⊗⇩s φ)"
  "_pure_tensor F ψ (_pure_tensor2 G φ H η)" ↽ "_pure_tensor2 F ψ (G;H) (φ ⊗⇩s η)"
term ‹(F ψ ⊗⇩p G φ ⊗⇩p H z)›
term ‹pure_state (F; G) (a ⊗⇩s b)›
lemma register_pair_butterfly_tensor: ‹(F; G) (butterfly (a ⊗⇩s b) (c ⊗⇩s d)) = F (butterfly a c) o⇩C⇩L G (butterfly b d)›
  if [simp]: ‹compatible F G›
  by (auto simp: default_prod_def simp flip: tensor_ell2_ket tensor_butterfly register_pair_apply)
lemma pure_state_eqI:
  assumes ‹F (selfbutter η⇩F) = G (selfbutter η⇩G)›
  assumes ‹F (butterfly ψ η⇩F) = G (butterfly φ η⇩G)›
  shows ‹pure_state' F ψ η⇩F = pure_state' G φ η⇩G›
proof -
  from assms(1) have ‹pure_state_target_vector F η⇩F = pure_state_target_vector G η⇩G›
    by (rule pure_state_target_vector_eqI)
  with assms(2)
  show ?thesis
    unfolding pure_state'_def
    by simp
qed
definition ‹regular_register F ⟷ register F ∧ (∃a. (F; complement F) (selfbutter (ket default) ⊗⇩o a) = selfbutter (ket default))›
lemma regular_registerI:
  assumes [simp]: ‹register F›
  assumes [simp]: ‹complements F G›
  assumes eq: ‹(F; G) (selfbutter (ket default) ⊗⇩o a) = selfbutter (ket default)›
  shows ‹regular_register F›
proof -
  have [simp]: ‹compatible F G›
    using assms by (simp add: complements_def)
  from ‹complements F G›
  obtain I where cFI: ‹complement F o I = G› and ‹iso_register I›
    apply atomize_elim
    using complement_unique' equivalent_registers_def equivalent_registers_sym by blast
  have ‹(F; complement F) (selfbutter (ket default) ⊗⇩o I a) = (F; G) (selfbutter (ket default) ⊗⇩o a)›
    using cFI by (auto simp: register_pair_apply)
  also have ‹… = selfbutter (ket default)›
    by (rule eq)
  finally show ?thesis
    unfolding regular_register_def by auto
qed
lemma regular_register_pair:
  assumes [simp]: ‹compatible F G›
  assumes ‹regular_register F› and ‹regular_register G›
  shows ‹regular_register (F;G)›
proof -
  have [simp]: ‹bij (F;complement F)› ‹bij (G;complement G)›
    using assms(1) compatible_def complement_is_complement complements_def iso_register_bij by blast+
  have bij_FGcFG[simp]: ‹bij ((F;G);complement (F;G))›
    using assms(1) complement_is_complement complements_def iso_register_bij pair_is_register by blast
  have [simp]: ‹register F› ‹register G›
    using assms(1) unfolding compatible_def by auto
  obtain aF where [simp]: ‹inv (F;complement F) (selfbutter (ket default)) = selfbutter (ket default) ⊗⇩o aF›
    by (metis assms(2) compatible_complement_right invI pair_is_register register_inj regular_register_def)
  obtain aG where [simp]: ‹inv (G;complement G) (selfbutter (ket default)) = selfbutter (ket default) ⊗⇩o aG›
    by (metis assms(3) complement_is_complement complements_def inj_iff inv_f_f iso_register_inv_comp1 regular_register_def)
  define t1 where ‹t1 = inv ((F;G); complement (F;G)) (selfbutter (ket default))›
  define t2 where ‹t2 = inv (F; (G; complement (F;G))) (selfbutter (ket default))›
  define t3 where ‹t3 = inv (G; (F; complement (F;G))) (selfbutter (ket default))›
  have ‹complements F (G; complement (F;G))›
    apply (rule complements_complement_pair)
    by simp_all
  then have ‹equivalent_registers (complement F) (G; complement (F;G))›
    using complement_unique' equivalent_registers_sym by blast
  then obtain I where [simp]: ‹iso_register I› and I: ‹(G; complement (F;G)) = complement F o I›
    by (metis equivalent_registers_def)
  then have [simp]: ‹register I›
    by (meson iso_register_is_register)
  have [simp]: ‹bij (id ⊗⇩r I)›
    by (rule iso_register_bij, simp)
  have [simp]: ‹inv (id ⊗⇩r I) = id ⊗⇩r inv I›
    by auto
  have ‹t2 = (inv (id ⊗⇩r I) o inv (F;complement F)) (selfbutter (ket default))›
    unfolding t2_def I
    apply (subst o_inv_distrib[symmetric]) 
    by (auto simp: pair_o_tensor)
  also have ‹… = (selfbutter (ket default) ⊗⇩o inv I aF)›
    apply simp
    by (metis ‹iso_register I› id_def iso_register_def iso_register_inv register_id register_tensor_apply)
  finally have t2': ‹t2 = selfbutter (ket default) ⊗⇩o inv I aF›
    by simp
  have *: ‹complements G (F; complement (F;G))›
    apply (rule complements_complement_pair')
    by simp_all
  then have [simp]: ‹compatible G (F; complement (F;G))›
    using complements_def by blast
  from * have ‹equivalent_registers (complement G) (F; complement (F;G))›
    using complement_unique' equivalent_registers_sym by blast
  then obtain J where [simp]: ‹iso_register J› and I: ‹(F; complement (F;G)) = complement G o J›
    by (metis equivalent_registers_def)
  then have [simp]: ‹register J›
    by (meson iso_register_is_register)
  have [simp]: ‹bij (id ⊗⇩r J)›
    by (rule iso_register_bij, simp)
  have [simp]: ‹inv (id ⊗⇩r J) = id ⊗⇩r inv J›
    by auto
  have ‹t3 = (inv (id ⊗⇩r J) o inv (G;complement G)) (selfbutter (ket default))›
    unfolding t3_def I
    apply (subst o_inv_distrib[symmetric]) 
    by (auto simp: pair_o_tensor)
  also have ‹… = (selfbutter (ket default) ⊗⇩o inv J aG)›
    apply simp
    by (metis ‹iso_register J› id_def iso_register_def iso_register_inv register_id register_tensor_apply)
  finally have t3': ‹t3 = selfbutter (ket default) ⊗⇩o inv J aG›
    by simp
  have *: ‹((F;G); complement (F;G)) o assoc' = (F; (G; complement (F;G)))›
    apply (rule tensor_extensionality3)
    by (auto simp: register_pair_apply  compatible_complement_pair1 compatible_complement_pair2)
  have t2_t1: ‹t2 = assoc t1›
    unfolding t1_def t2_def *[symmetric] apply (subst o_inv_distrib)
    by auto
  have *: ‹((F;G); complement (F;G)) o (swap ⊗⇩r id) o assoc' = (G; (F; complement (F;G)))›
    apply (rule tensor_extensionality3)
    by (auto intro!: register_comp register_tensor_is_register pair_is_register complements_complement_pair
        simp: register_pair_apply compatible_complement_pair1 
        lift_cblinfun_comp[OF swap_registers_left, of F G] cblinfun_assoc_left)
  have t3_t1: ‹t3 = assoc ((swap ⊗⇩r id) t1)›
    unfolding t1_def t3_def *[symmetric] apply (subst o_inv_distrib)
    by (auto intro!: bij_comp simp: iso_register_bij o_inv_distrib)
  from ‹t2 = assoc t1› ‹t3 = assoc ((swap ⊗⇩r id) t1)›
  have *: ‹selfbutter (ket default) ⊗⇩o inv J aG =  assoc ((swap ⊗⇩r id) (assoc' (selfbutter (ket default) ⊗⇩o inv I aF)))›
    by (simp add: t2' t3')
  have ‹selfbutter (ket default) ⊗⇩o swap (inv J aG) = (id ⊗⇩r swap) (selfbutter (ket default) ⊗⇩o inv J aG)›
    by auto
  also have ‹… = ((id ⊗⇩r swap) o assoc o (swap ⊗⇩r id) o assoc') (selfbutter (ket default) ⊗⇩o inv I aF)›
    by (simp add: *)
  also have ‹… = (assoc o swap) (selfbutter (ket default) ⊗⇩o inv I aF)›
    apply (rule fun_cong[where g=‹assoc o swap›])
    apply (intro tensor_extensionality3 register_comp register_tensor_is_register)
    by auto
  also have ‹… = assoc (inv I aF ⊗⇩o selfbutter (ket default))›
    by auto
  finally have *: ‹selfbutter (ket default) ⊗⇩o swap (inv J aG) = assoc (inv I aF ⊗⇩o selfbutter (ket default))›
    by -
  obtain c where *: ‹butterfly (ket default :: 'c ell2) (ket default :: 'c ell2) ⊗⇩o swap (inv J aG) = selfbutter (ket default) ⊗⇩o c ⊗⇩o selfbutter (ket default)›
    apply atomize_elim
    apply (rule overlapping_tensor)
    using * unfolding assoc_ell2_sandwich sandwich_apply
    by auto
  have ‹t1 = ((swap ⊗⇩r id) o assoc') t3›
    by (simp add: t3_t1 register_tensor_distrib[unfolded o_def, THEN fun_cong] flip: id_def)
  also have ‹… = ((swap ⊗⇩r id) o assoc' o (id ⊗⇩r swap)) (butterfly (ket default :: 'c ell2) (ket default :: 'c ell2) ⊗⇩o swap (inv J aG))›
    unfolding t3' by auto
  also have ‹… = ((swap ⊗⇩r id) o assoc' o (id ⊗⇩r swap)) (selfbutter (ket default) ⊗⇩o c ⊗⇩o selfbutter (ket default))›
    unfolding * by simp
  also have ‹… = selfbutter (ket default) ⊗⇩o c›
    apply simp
    by (simp add: default_prod_def tensor_butterfly tensor_ell2_ket)
  finally have ‹t1 = selfbutter (ket default) ⊗⇩o c›
    by -
  then show ?thesis
    by (auto intro!: exI[of _ c] simp: regular_register_def t1_def 
        simp flip: bij_inv_eq_iff[OF bij_FGcFG])
qed
lemma regular_register_comp: ‹regular_register (F o G)› if ‹regular_register F› ‹regular_register G›
proof -
  have [simp]: ‹register F› ‹register G›
    using regular_register_def that by blast+
  from that obtain a where a: ‹(F; complement F) (selfbutter (ket default) ⊗⇩o a) = selfbutter (ket default)›
    unfolding regular_register_def by metis
  from that obtain b where b: ‹(G; complement G) (selfbutter (ket default) ⊗⇩o b) = selfbutter (ket default)›
    unfolding regular_register_def by metis
  have ‹complements (F o G) (complement F; F o complement G)›
    by (simp add: complements_chain)
  then have ‹equivalent_registers (complement F; F o complement G) (complement (F o G))›
    using complement_unique' by blast
  then obtain J where [simp]: ‹iso_register J› and 1: ‹(complement F; F o complement G) o J = (complement (F o G))›
    using equivalent_registers_def by blast
  have [simp]: ‹register J›
    by (simp add: iso_register_is_register)
  define c where ‹c = inv J (a ⊗⇩o b)›
  have ‹((F o G); complement (F o G)) (selfbutter (ket default) ⊗⇩o c) = ((F o G); (complement F; F o complement G)) (selfbutter (ket default) ⊗⇩o J c)›
    by (auto simp flip: 1 simp: register_pair_apply)
  also have ‹… = ((F o (G; complement G); complement F) o assoc' o (id ⊗⇩r swap)) (selfbutter (ket default) ⊗⇩o J c)›
    apply (subst register_comp_pair[symmetric])
      apply auto[2]
    apply (subst pair_o_assoc')
       apply auto[3]
    apply (subst pair_o_tensor)
    by auto
  also have ‹… = ((F o (G; complement G); complement F) o assoc') (selfbutter (ket default) ⊗⇩o swap (J c))›
    by auto
  also have ‹… = ((F o (G; complement G); complement F) o assoc') (selfbutter (ket default) ⊗⇩o (b ⊗⇩o a))›
    unfolding c_def apply (subst surj_f_inv_f[where f=J])
     apply (meson ‹iso_register J› bij_betw_inv_into_right iso_register_inv_comp1 iso_register_inv_comp2 iso_tuple_UNIV_I o_bij surj_iff_all)
    by auto
  also have ‹… = (F ∘ (G;complement G);complement F) ((selfbutter (ket default) ⊗⇩o b) ⊗⇩o a)›
    by (simp add: assoc'_apply)
  also have ‹… = (F; complement F) ((G;complement G) (selfbutter (ket default) ⊗⇩o b) ⊗⇩o a)›
    by (simp add: register_pair_apply')
  also have ‹… = selfbutter (ket default)›
    by (auto simp: a b) 
  finally have ‹(F ∘ G;complement (F ∘ G)) (selfbutter (ket default) ⊗⇩o c) = selfbutter (ket default)›
    by -
  then show ?thesis
    using ‹register F› ‹register G› register_comp regular_register_def by blast
qed
lemma regular_iso_register:
  assumes ‹regular_register F›
  assumes [register]: ‹iso_register F›
  shows ‹F (selfbutter (ket default)) = selfbutter (ket default)›
proof -
  from assms(1) obtain a where a: ‹(F;complement F) (selfbutter (ket default) ⊗⇩o a) = selfbutter (ket default)›
    using regular_register_def by blast
  let ?u = ‹empty_var :: (unit ell2 ⇒⇩C⇩L unit ell2) ⇒ _›
  have ‹is_unit_register ?u› and ‹is_unit_register (complement F)›
    by auto
  then have ‹equivalent_registers (complement F) ?u›
    using unit_register_unique by blast
  then obtain I where ‹iso_register I› and ‹complement F = ?u o I›
    by (metis ‹is_unit_register (complement F)› equivalent_registers_def is_unit_register_empty_var unit_register_unique)
  have ‹selfbutter (ket default) = (F; ?u o I) (selfbutter (ket default) ⊗⇩o a)›
    using ‹complement F = empty_var ∘ I› a by presburger
  also have ‹… = (F; ?u) (selfbutter (ket default) ⊗⇩o I a)›
    by (metis Laws_Quantum.register_pair_apply ‹complement F = empty_var ∘ I› ‹equivalent_registers (complement F) empty_var› assms(2) comp_apply complement_is_complement complements_def equivalent_complements iso_register_is_register)
  also have ‹… = (F; ?u) (selfbutter (ket default) ⊗⇩o (one_dim_iso (I a) *⇩C id_cblinfun))›
    by simp
  also have ‹… = one_dim_iso (I a) *⇩C (F; ?u) (selfbutter (ket default) ⊗⇩o id_cblinfun)›
    by (simp add: Axioms_Quantum.register_pair_apply empty_var_def iso_register_is_register)
  also have ‹… = one_dim_iso (I a) *⇩C F (selfbutter (ket default))›
    by (auto simp: register_pair_apply iso_register_is_register simp del: id_cblinfun_eq_1)
  finally have F: ‹one_dim_iso (I a) *⇩C F (selfbutter (ket default)) = selfbutter (ket default)›
    by simp
  from F have ‹one_dim_iso (I a) ≠ (0::complex)›
    by (metis butterfly_apply butterfly_scaleC_left complex_vector.scale_eq_0_iff id_cblinfun_eq_1 id_cblinfun_not_0 cinner_ket_same ket_nonzero one_dim_iso_of_one one_dim_iso_of_zero')
  have ‹selfbutter (ket default) = one_dim_iso (I a) *⇩C F (selfbutter (ket default))›
    using F by simp
  also have ‹… = one_dim_iso (I a) *⇩C F (selfbutter (ket default) o⇩C⇩L selfbutter (ket default))›
    by auto
  also have ‹… = one_dim_iso (I a) *⇩C (F (selfbutter (ket default)) o⇩C⇩L F (selfbutter (ket default)))›
    by (simp add: assms(2) iso_register_is_register register_mult)
  also have ‹… = one_dim_iso (I a) *⇩C ((selfbutter (ket default) /⇩C one_dim_iso (I a)) o⇩C⇩L (selfbutter (ket default) /⇩C one_dim_iso (I a)))›
    by (metis (no_types, lifting) F ‹one_dim_iso (I a) ≠ 0› complex_vector.scale_left_imp_eq inverse_1 left_inverse scaleC_scaleC zero_neq_one)
  also have ‹… = one_dim_iso (I a) *⇩C selfbutter (ket default)›
    by (smt (verit, best) butterfly_comp_butterfly calculation cblinfun_compose_scaleC_left cblinfun_compose_scaleC_right complex_vector.scale_cancel_left cinner_ket_same left_inverse scaleC_one scaleC_scaleC)
  finally have ‹one_dim_iso (I a) = (1::complex)›
    by (metis butterfly_0_left butterfly_apply complex_vector.scale_cancel_right cinner_ket_same ket_nonzero scaleC_one)
  with F show ‹F (selfbutter (ket default)) = selfbutter (ket default)›
    by simp
qed
lemma pure_state_nested:
  assumes [simp]: ‹compatible F G›
  assumes ‹regular_register H›
  assumes ‹iso_register H›
  shows ‹pure_state (F;G) (pure_state H h ⊗⇩s g) = pure_state ((F o H);G) (h ⊗⇩s g)›
proof -
  note [[simproc del: Laws_Quantum.compatibility_warn]]
  have [simp]: ‹register H›
    by (meson assms(3) iso_register_is_register)
  have [simp]: ‹H (selfbutter (ket default)) = selfbutter (ket default)›
    apply (rule regular_iso_register)
    using assms by auto
  have 1: ‹pure_state_target_vector H (ket default) = ket default›
    apply (rule pure_state_target_vector_ket_default)
    apply simp
    by (metis (no_types, lifting) cinner_ket_same rangeI scaleC_one)
  have ‹butterfly (pure_state H h) (ket default) = butterfly (H (butterfly h (ket default)) *⇩V ket default) (ket default)›
    by (simp add: pure_state'_def 1)
  also have ‹… = H (butterfly h (ket default)) o⇩C⇩L selfbutter (ket default)›
    by (metis (no_types, opaque_lifting) adj_cblinfun_compose butterfly_adjoint butterfly_comp_cblinfun double_adj)
  also have ‹… = H (butterfly h (ket default)) o⇩C⇩L H (selfbutter (ket default))›
    by simp
  also have ‹… = H (butterfly h (ket default) o⇩C⇩L selfbutter (ket default))›
    by (meson ‹register H› register_mult)
  also have ‹… = H (butterfly h (ket default))›
    by auto
  finally have 2: ‹butterfly (pure_state H h) (ket default) = H (butterfly h (ket default))›
    by simp
  show ?thesis
    apply (rule pure_state_eqI)
    using 1 2
    by (auto simp: register_pair_butterfly_tensor compatible_ac_rules default_prod_def simp flip: tensor_ell2_ket)
qed
lemma state_apply1: 
  assumes [register]: ‹compatible F G›
  shows ‹F U *⇩V (F ψ ⊗⇩p G φ) = (F (U ψ) ⊗⇩p G φ)›
proof -
  have [register]: ‹compatible F G›
    using assms(1) complements_def by blast
  have ‹F U *⇩V (F ψ ⊗⇩p G φ) = (F;G) (U ⊗⇩o id_cblinfun) *⇩V (F ψ ⊗⇩p G φ)›
    apply (subst register_pair_apply)
    by auto
  also have ‹… = (F (U ψ) ⊗⇩p G φ)›
    unfolding pure_state'_def 
    by (auto simp: register_mult' cblinfun_comp_butterfly tensor_op_ell2)
  finally show ?thesis
    by -
qed
lemma Fst_regular[simp]: ‹regular_register Fst›
  apply (rule regular_registerI[where a=‹selfbutter (ket default)› and G=Snd])
  by (auto simp: pair_Fst_Snd default_prod_def tensor_ell2_ket tensor_butterfly)
lemma Snd_regular[simp]: ‹regular_register Snd›
  apply (rule regular_registerI[where a=‹selfbutter (ket default)› and G=Fst])
    apply auto[2]
  apply (simp only: default_prod_def swap_apply flip: swap_def tensor_ell2_ket)
  by (auto simp: tensor_butterfly)
lemma id_regular[simp]: ‹regular_register id›
  apply (rule regular_registerI[where G=unit_register and a=id_cblinfun])
  by (auto simp: register_pair_apply)
lemma swap_regular[simp]: ‹regular_register swap›
  by (auto intro!: regular_register_pair simp: swap_def)
lemma assoc_regular[simp]: ‹regular_register assoc›
  by (auto intro!: regular_register_pair regular_register_comp simp: assoc_def)
lemma assoc'_regular[simp]: ‹regular_register assoc'›
  by (auto intro!: regular_register_pair regular_register_comp simp: assoc'_def)
lemma cspan_pure_state': 
  assumes ‹iso_register F›
  assumes ‹cspan (g ` X) = UNIV›
  assumes η_cond: ‹F (selfbutter η) *⇩V pure_state_target_vector F η ≠ 0›
  shows ‹cspan ((λz. pure_state' F (g z) η) ` X) = UNIV›
proof -
  from iso_register_decomposition[of F]
  obtain U where [simp]: ‹unitary U› and F: ‹F = sandwich U›
    using assms(1) by blast
  define η' c where ‹η' = pure_state_target_vector F η› and ‹c = cinner (U *⇩V η) η'›
  from η_cond
  have ‹c ≠ 0›
    by (simp add: η'_def F sandwich_apply c_def cinner_adj_right cblinfun.scaleC_right)
  have ‹cspan ((λz. pure_state' F (g z) η) ` X) = cspan ((λz. F (butterfly (g z) η) *⇩V η') ` X)›
    by (simp add: η'_def pure_state'_def)
  also have ‹… = cspan ((λz. (butterfly (U *⇩V g z) (U *⇩V η)) *⇩V η') ` X)›
    by (simp add: F sandwich_apply cinner_adj_right cblinfun.scaleC_right)
  also have ‹… = cspan ((λz. c *⇩C U *⇩V g z) ` X)›
    by (simp add: c_def)
  also have ‹… = (λz. c *⇩C U *⇩V z) ` cspan (g ` X)›
    apply (subst complex_vector.linear_span_image[symmetric])
    by (auto simp: image_image)
  also have ‹… = (λz. c *⇩C U *⇩V z) ` UNIV›
    using assms(2) by presburger
  also have ‹… = UNIV›
    apply (rule surjI[where f=‹λz. (U* *⇩V z) /⇩C c›])
    using ‹c ≠ 0› by (auto simp flip: cblinfun_apply_cblinfun_compose cblinfun.scaleC_right)
  finally show ?thesis
    by -
qed
lemma cspan_pure_state: 
  assumes [simp]: ‹iso_register F›
  assumes ‹cspan (g ` X) = UNIV›
  shows ‹cspan ((λz. pure_state F (g z)) ` X) = UNIV›
  apply (rule cspan_pure_state')
  using assms apply auto[2]
  apply (rule pure_state_target_vector_correct)
  by (auto simp: iso_register_is_register)
lemma pure_state_bounded_clinear:
  assumes [register]: ‹compatible F G›
  shows ‹bounded_clinear (λψ. (F ψ ⊗⇩p G φ))›
proof -
  have [bounded_clinear]: ‹bounded_clinear (F;G)›
    using assms pair_is_register register_bounded_clinear by blast
  show ?thesis
    unfolding pure_state'_def
    by (auto intro!: bounded_linear_intros)
qed
lemma pure_state_bounded_clinear_right:
  assumes [register]: ‹compatible F G›
  shows ‹bounded_clinear (λφ. (F ψ ⊗⇩p G φ))›
proof -
  have [bounded_clinear]: ‹bounded_clinear (F;G)›
    using assms pair_is_register register_bounded_clinear by blast
  show ?thesis
    unfolding pure_state'_def
    by (auto intro!: bounded_linear_intros)
qed
lemma pure_state_clinear:
  assumes [register]: ‹compatible F G›
  shows ‹clinear (λψ. (F ψ ⊗⇩p G φ))›
  using assms bounded_clinear.clinear pure_state_bounded_clinear by blast
method pure_state_flatten_nested =
  (subst pure_state_nested, (auto; fail)[3])+
text ‹The following method ‹pure_state_eq› tries to solve a equality where both sides are of the form
  ‹F⇩1(ψ⇩1) ⊗⇩p F⇩2(ψ⇩2) ⊗⇩p … ⊗⇩p F⇩n(ψ⇩n)› by reordering the registers and unfolding nested register pairs.
  (For the unfolding of nested pairs, it is necessary that the corresponding \<^term>‹compatible F G› facts are provable by the simplifier.)
  If the some of the pure states \<^term>‹ψ⇩i› themselves are ‹⊗⇩p›-tensors, they will be flattened if possible. 
  (If all necessary conditions can be proven, such as ‹regular_register› etc.)
  The method may either succeed, fail, or reduce the equality to a hopefully simpler one.›
method pure_state_eq =
  (pure_state_flatten_nested?,
    rule pure_state_eqI;
    auto simp: register_pair_butterfly_tensor compatible_ac_rules default_prod_def
    simp flip: tensor_ell2_ket)
lemma example:
  fixes F :: ‹bit update ⇒ 'c::{finite,default} update›
    and G :: ‹bit update ⇒ 'c update›
  assumes [register]: ‹compatible F G›
  shows  ‹(F;G) CNOT o⇩C⇩L (G;F) CNOT o⇩C⇩L (F;G) CNOT = (F;G) swap_ell2›
proof -
  define Z where ‹Z = complement (F;G)›
  then have [register]: ‹compatible Z F› ‹compatible Z G›
    using assms compatible_complement_pair1 compatible_complement_pair2 compatible_sym by blast+
  have [simp]: ‹iso_register (F;(G;Z))›
    using Z_def assms complement_is_complement complements_complement_pair complements_def pair_is_register by blast
  have eq1: ‹((F;G) CNOT o⇩C⇩L (G;F) CNOT o⇩C⇩L (F;G) CNOT) *⇩V (F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z))
           = (F;G) swap_ell2 *⇩V (F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z))› for f g z
  proof -
    have ‹(F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z)) = ((F;G) (ket f ⊗⇩s ket g) ⊗⇩p Z(ket z))›
      by pure_state_eq
    also have ‹(F;G) CNOT *⇩V … = ((F;G) (ket f ⊗⇩s ket (g+f)) ⊗⇩p Z(ket z))›
      apply (subst state_apply1) by (auto simp: tensor_ell2_ket)
    also have ‹… = ((G;F) (ket (g+f) ⊗⇩s ket f) ⊗⇩p Z(ket z))›
      by pure_state_eq
    also have ‹(G;F) CNOT *⇩V … = ((G;F) (ket (g+f) ⊗⇩s ket g) ⊗⇩p Z ket z)›
      apply (subst state_apply1) by (auto simp: tensor_ell2_ket)
    also have ‹… = ((F;G) (ket g ⊗⇩s ket (g+f)) ⊗⇩p Z ket z)›
      by pure_state_eq
    also have ‹(F;G) CNOT *⇩V … = ((F;G) ket g ⊗⇩s ket f ⊗⇩p Z ket z)›
      apply (subst state_apply1)
       apply simp
      by (metis add_diff_cancel_left' cnot_apply minus_bit_def tensor_ell2_ket)
    also have ‹… = (F(ket g) ⊗⇩p G(ket f) ⊗⇩p Z(ket z))›
      by pure_state_eq
    finally have 1: ‹((F;G) CNOT o⇩C⇩L (G;F) CNOT o⇩C⇩L (F;G) CNOT) *⇩V (F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z)) = (F(ket g) ⊗⇩p G(ket f) ⊗⇩p Z(ket z))›
      by auto
    have ‹(F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z)) = ((F;G) (ket f ⊗⇩s ket g) ⊗⇩p Z(ket z))›
      by pure_state_eq
    also have ‹(F;G) swap_ell2 *⇩V … = ((F;G) (ket g ⊗⇩s ket f) ⊗⇩p Z(ket z))›
      by (auto simp: state_apply1 swap_ell2_tensor simp del: tensor_ell2_ket)
    also have ‹… = (F(ket g) ⊗⇩p G(ket f) ⊗⇩p Z(ket z))›
      by pure_state_eq
    finally have 2: ‹(F;G) swap_ell2 *⇩V (F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z)) = (F(ket g) ⊗⇩p G(ket f) ⊗⇩p Z(ket z))›
      by -
    from 1 2 show ?thesis
      by simp
  qed
  then have eq1: ‹((F;G) CNOT o⇩C⇩L (G;F) CNOT o⇩C⇩L (F;G) CNOT) *⇩V ψ
           = (F;G) swap_ell2 *⇩V ψ› if ‹ψ ∈ {(F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z))| f g z. True}› for ψ
    using that by auto
  moreover have ‹cspan {(F(ket f) ⊗⇩p G(ket g) ⊗⇩p Z(ket z))| f g z. True} = UNIV›
    apply (simp only: double_exists setcompr_eq_image full_SetCompr_eq)
    apply simp
    apply (rule cspan_pure_state)
    by (auto simp: tensor_ell2_ket)
  ultimately show ?thesis
    using cblinfun_eq_on_UNIV_span by blast
qed
unbundle no cblinfun_syntax
unbundle no register_syntax
end