From 267012d9a8c4c518b813ad506b299529730c6c53 Mon Sep 17 00:00:00 2001 From: apkille Date: Sun, 11 May 2025 01:26:51 -0400 Subject: [PATCH 01/12] add new QHO states --- src/QSymbolicsBase/QSymbolicsBase.jl | 3 ++- src/QSymbolicsBase/predefined_fock.jl | 38 ++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 84a886f..a17bdc4 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -43,8 +43,9 @@ export SymQObj,QObj, MixedState,IdentityOp, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, - XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState, + XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,EPRState,ThermalState, NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp, + TwoSqueezeOp,BeamSplitterOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock, qexpand, diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index b3294d5..7fa94fb 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -26,6 +26,14 @@ end SqueezedState(z::Number) = SqueezedState(z, inf_fock_basis) symbollabel(x::SqueezedState) = "0,$(x.z)" +"""Two-mode squeezed vacuum state, or EPR state, in defined Fock basis.""" +@withmetadata struct EPRState <: SpecialKet + z::Number + basis::FockBasis +end +EPRState(z::Number) = EPRState(z, inf_fock_basis) +symbollabel(x::EPRState) = "0,$(x.z)" + const inf_fock_basis = FockBasis(Inf,0.) """Vacuum basis state of n""" const vac = const F₀ = const F0 = FockState(0) @@ -37,9 +45,13 @@ const F₁ = const F1 = FockState(1) ## abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end +abstract type AbstractTwoBosonOp <: Symbolic{AbstractOperator} end abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice +abstract type AbstractTwoBosonGate <: AbstractTwoBosonOp end isexpr(::AbstractSingleBosonGate) = false basis(x::AbstractSingleBosonOp) = inf_fock_basis +isexpr(::AbstractTwoBosonGate) = false +basis(x::AbstractTwoBosonOp) = inf_fock_basis^2 """Number operator. @@ -161,4 +173,28 @@ julia> qsimplify(S*vac, rewriter=qsimplify_fock) basis::FockBasis end SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis) -symbollabel(x::SqueezeOp) = "S($(x.z))" \ No newline at end of file +symbollabel(x::SqueezeOp) = "S($(x.z))" + +"""Thermal bosonic state in defined Fock basis.""" +@withmetadata struct ThermalState <: AbstractSingleBosonOp + photons::Int + basis::FockBasis +end +ThermalState(photons::Int) = ThermalState(photons, inf_fock_basis) +symbollabel(x::ThermalState) = "ρₜₕ($(x.photons))" + +"""Two-mode squeezing operator in defined Fock basis.""" +@withmetadata struct TwoSqueezeOp <: AbstractTwoBosonOp + z::Number + basis::CompositeBasis +end +TwoSqueezeOp(z::Number) = TwoSqueezeOp(z, inf_fock_basis^2) +symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))" + +"""Two-mode beamsplitter operator in defined Fock basis.""" +@withmetadata struct BeamSplitterOp <: AbstractTwoBosonOp + transmit::Number + basis::CompositeBasis +end +BeamSplitterOp(transmit::Number) = BeamSplitterOp(transmit, inf_fock_basis^2) +symbollabel(x::BeamSplitterOp) = "B($(x.transmit))" \ No newline at end of file From d8171d387ec3a4b377f02029579f5641ddda69ac Mon Sep 17 00:00:00 2001 From: apkille Date: Sun, 11 May 2025 03:41:00 -0400 Subject: [PATCH 02/12] add error messages and tests --- src/QSymbolicsBase/predefined_fock.jl | 25 +++++++++++++++++++++++-- src/QSymbolicsBase/rules.jl | 5 +++-- test/test_fock.jl | 3 +++ test/test_throws.jl | 4 ++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 7fa94fb..0c4385c 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -29,9 +29,16 @@ symbollabel(x::SqueezedState) = "0,$(x.z)" """Two-mode squeezed vacuum state, or EPR state, in defined Fock basis.""" @withmetadata struct EPRState <: SpecialKet z::Number - basis::FockBasis + basis::CompositeBasis + function EPRState(z::Number, basis::CompositeBasis) + bases = basis.bases + length(bases) == 2 && all(x -> x isa FockBasis, bases) || + throw(ArgumentError(lazy"The underlying basis for an EPR state must be a + tensor product of two bases for single-mode quantum systems.")) + return new(z, basis) + end end -EPRState(z::Number) = EPRState(z, inf_fock_basis) +EPRState(z::Number) = EPRState(z, inf_fock_basis^2) symbollabel(x::EPRState) = "0,$(x.z)" const inf_fock_basis = FockBasis(Inf,0.) @@ -187,6 +194,13 @@ symbollabel(x::ThermalState) = "ρₜₕ($(x.photons))" @withmetadata struct TwoSqueezeOp <: AbstractTwoBosonOp z::Number basis::CompositeBasis + function TwoSqueezeOp(z::Number, basis::CompositeBasis) + bases = basis.bases + length(bases) == 2 && all(x -> x isa FockBasis, bases) || + throw(ArgumentError(lazy"The underlying basis for a two-mode squeeze operator must be a + tensor product of two bases for single-mode quantum systems.")) + return new(z, basis) + end end TwoSqueezeOp(z::Number) = TwoSqueezeOp(z, inf_fock_basis^2) symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))" @@ -195,6 +209,13 @@ symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))" @withmetadata struct BeamSplitterOp <: AbstractTwoBosonOp transmit::Number basis::CompositeBasis + function BeamSplitterOp(transmit::Number, basis::CompositeBasis) + bases = basis.bases + length(bases) == 2 && all(x -> x isa FockBasis, bases) || + throw(ArgumentError(lazy"The underlying basis for a beam splitter operator must be a + tensor product of two bases for single-mode quantum systems.")) + return new(transmit, basis) + end end BeamSplitterOp(transmit::Number) = BeamSplitterOp(transmit, inf_fock_basis^2) symbollabel(x::BeamSplitterOp) = "B($(x.transmit))" \ No newline at end of file diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 9ae6fda..8c4ff2a 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -11,7 +11,7 @@ function hasscalings(xs) end end _isa(T) = x->isa(x,T) -_isequal(obj) = x->(x==obj) +_isequal(obj) = x->isequal(x, obj) _vecisa(T) = x->all(_isa(T), x) ## @@ -106,7 +106,8 @@ RULES_FOCK = [ @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)), @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), @rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)), - @rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis)) + @rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis)), + @rule(~o::_isa(TwoSqueezeOp) * ~k::_isequal(vac ⊗ vac) => EPRState((~o).z, (~o).basis)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/test_fock.jl b/test/test_fock.jl index 33a7495..ba7c302 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -7,7 +7,9 @@ phase2 = PhaseShiftOp(pi) displace = DisplaceOp(im) squeezeop = SqueezeOp(pi) + twosqueezeop = TwoSqueezeOp(pi) sstate = SqueezedState(pi) + tsstate = EPRState(pi) @testset "ladder and number operators" begin @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) @@ -28,5 +30,6 @@ @testset "Squeeze operators" begin @test isequal(qsimplify(squeezeop*vac, rewriter=qsimplify_fock), sstate) + @test isequal(qsimplify(twosqueezeop*(vac ⊗ vac), rewriter=qsimplify_fock), tsstate) end end \ No newline at end of file diff --git a/test/test_throws.jl b/test/test_throws.jl index c2f9cc6..20e70b0 100644 --- a/test/test_throws.jl +++ b/test/test_throws.jl @@ -4,6 +4,7 @@ @op A SpinBasis(1//2) ⊗ SpinBasis(1//2); @op B; @op C; @ket k; @bra b; @ket l SpinBasis(1//2) ⊗ SpinBasis(1//2); + fb = FockBasis(10) @test_throws IncompatibleBases A*B @test_throws IncompatibleBases commutator(A, B) @@ -14,4 +15,7 @@ @test_throws IncompatibleBases b*l @test_throws ArgumentError ptrace(B, 2) @test_throws ArgumentError ptrace(B+C, 2) + @test_throws ArgumentError EPRState(pi, fb^3) + @test_throws ArgumentError TwoSqueezeOp(pi, fb^3) + @test_throws ArgumentError BeamSplitterOp(pi, fb^3) end \ No newline at end of file From ea21e5e4d2ea934447d5bfaf84e94fdba9a9398d Mon Sep 17 00:00:00 2001 From: apkille Date: Sun, 11 May 2025 21:37:57 -0400 Subject: [PATCH 03/12] make error messages appear on a single line --- src/QSymbolicsBase/predefined_fock.jl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 0c4385c..525a68d 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -33,8 +33,7 @@ symbollabel(x::SqueezedState) = "0,$(x.z)" function EPRState(z::Number, basis::CompositeBasis) bases = basis.bases length(bases) == 2 && all(x -> x isa FockBasis, bases) || - throw(ArgumentError(lazy"The underlying basis for an EPR state must be a - tensor product of two bases for single-mode quantum systems.")) + throw(ArgumentError(lazy"The underlying basis for an EPR state must be a tensor product of two bases for single-mode quantum systems.")) return new(z, basis) end end @@ -197,8 +196,7 @@ symbollabel(x::ThermalState) = "ρₜₕ($(x.photons))" function TwoSqueezeOp(z::Number, basis::CompositeBasis) bases = basis.bases length(bases) == 2 && all(x -> x isa FockBasis, bases) || - throw(ArgumentError(lazy"The underlying basis for a two-mode squeeze operator must be a - tensor product of two bases for single-mode quantum systems.")) + throw(ArgumentError(lazy"The underlying basis for a two-mode squeeze operator must be a tensor product of two bases for single-mode quantum systems.")) return new(z, basis) end end @@ -212,8 +210,7 @@ symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))" function BeamSplitterOp(transmit::Number, basis::CompositeBasis) bases = basis.bases length(bases) == 2 && all(x -> x isa FockBasis, bases) || - throw(ArgumentError(lazy"The underlying basis for a beam splitter operator must be a - tensor product of two bases for single-mode quantum systems.")) + throw(ArgumentError(lazy"The underlying basis for a beam splitter operator must be a tensor product of two bases for single-mode quantum systems.")) return new(transmit, basis) end end From 318fcdde39ab67ae131b830383d275feb0fd868b Mon Sep 17 00:00:00 2001 From: apkille Date: Sun, 11 May 2025 22:17:15 -0400 Subject: [PATCH 04/12] change names of EPRState and ThermalState --- src/QSymbolicsBase/QSymbolicsBase.jl | 2 +- src/QSymbolicsBase/predefined_fock.jl | 14 +++++++------- src/QSymbolicsBase/rules.jl | 2 +- test/test_fock.jl | 2 +- test/test_throws.jl | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index a17bdc4..6f73ffc 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -43,7 +43,7 @@ export SymQObj,QObj, MixedState,IdentityOp, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, - XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,EPRState,ThermalState, + XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,TwoSqueezedState,BosonicThermalState, NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp, TwoSqueezeOp,BeamSplitterOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 525a68d..f272bac 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -27,18 +27,18 @@ SqueezedState(z::Number) = SqueezedState(z, inf_fock_basis) symbollabel(x::SqueezedState) = "0,$(x.z)" """Two-mode squeezed vacuum state, or EPR state, in defined Fock basis.""" -@withmetadata struct EPRState <: SpecialKet +@withmetadata struct TwoSqueezedState <: SpecialKet z::Number basis::CompositeBasis - function EPRState(z::Number, basis::CompositeBasis) + function TwoSqueezedState(z::Number, basis::CompositeBasis) bases = basis.bases length(bases) == 2 && all(x -> x isa FockBasis, bases) || throw(ArgumentError(lazy"The underlying basis for an EPR state must be a tensor product of two bases for single-mode quantum systems.")) return new(z, basis) end end -EPRState(z::Number) = EPRState(z, inf_fock_basis^2) -symbollabel(x::EPRState) = "0,$(x.z)" +TwoSqueezedState(z::Number) = TwoSqueezedState(z, inf_fock_basis^2) +symbollabel(x::TwoSqueezedState) = "0,$(x.z)" const inf_fock_basis = FockBasis(Inf,0.) """Vacuum basis state of n""" @@ -182,12 +182,12 @@ SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis) symbollabel(x::SqueezeOp) = "S($(x.z))" """Thermal bosonic state in defined Fock basis.""" -@withmetadata struct ThermalState <: AbstractSingleBosonOp +@withmetadata struct BosonicThermalState <: AbstractSingleBosonOp photons::Int basis::FockBasis end -ThermalState(photons::Int) = ThermalState(photons, inf_fock_basis) -symbollabel(x::ThermalState) = "ρₜₕ($(x.photons))" +BosonicThermalState(photons::Int) = BosonicThermalState(photons, inf_fock_basis) +symbollabel(x::BosonicThermalState) = "ρₜₕ($(x.photons))" """Two-mode squeezing operator in defined Fock basis.""" @withmetadata struct TwoSqueezeOp <: AbstractTwoBosonOp diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 8c4ff2a..7e796c4 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -107,7 +107,7 @@ RULES_FOCK = [ @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), @rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)), @rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis)), - @rule(~o::_isa(TwoSqueezeOp) * ~k::_isequal(vac ⊗ vac) => EPRState((~o).z, (~o).basis)) + @rule(~o::_isa(TwoSqueezeOp) * ~k::_isequal(vac ⊗ vac) => TwoSqueezedState((~o).z, (~o).basis)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/test_fock.jl b/test/test_fock.jl index ba7c302..d4db1ef 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -9,7 +9,7 @@ squeezeop = SqueezeOp(pi) twosqueezeop = TwoSqueezeOp(pi) sstate = SqueezedState(pi) - tsstate = EPRState(pi) + tsstate = TwoSqueezedState(pi) @testset "ladder and number operators" begin @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) diff --git a/test/test_throws.jl b/test/test_throws.jl index 20e70b0..8e26283 100644 --- a/test/test_throws.jl +++ b/test/test_throws.jl @@ -15,7 +15,7 @@ @test_throws IncompatibleBases b*l @test_throws ArgumentError ptrace(B, 2) @test_throws ArgumentError ptrace(B+C, 2) - @test_throws ArgumentError EPRState(pi, fb^3) + @test_throws ArgumentError TwoSqueezedState(pi, fb^3) @test_throws ArgumentError TwoSqueezeOp(pi, fb^3) @test_throws ArgumentError BeamSplitterOp(pi, fb^3) end \ No newline at end of file From 73943327b7cf625e54b422728b819d5b07d82e00 Mon Sep 17 00:00:00 2001 From: apkille Date: Sun, 11 May 2025 22:19:04 -0400 Subject: [PATCH 05/12] parametrize .photons field with Number --- src/QSymbolicsBase/predefined_fock.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index f272bac..22997c9 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -183,10 +183,10 @@ symbollabel(x::SqueezeOp) = "S($(x.z))" """Thermal bosonic state in defined Fock basis.""" @withmetadata struct BosonicThermalState <: AbstractSingleBosonOp - photons::Int + photons::Number basis::FockBasis end -BosonicThermalState(photons::Int) = BosonicThermalState(photons, inf_fock_basis) +BosonicThermalState(photons::Number) = BosonicThermalState(photons, inf_fock_basis) symbollabel(x::BosonicThermalState) = "ρₜₕ($(x.photons))" """Two-mode squeezing operator in defined Fock basis.""" From 3bb2ce65281eabe312a13120267b77a6b03278e4 Mon Sep 17 00:00:00 2001 From: apkille Date: Sun, 11 May 2025 22:37:18 -0400 Subject: [PATCH 06/12] update changelog and project.toml to v0.4.11-dev --- CHANGELOG.md | 4 ++++ Project.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1851f3f..4503079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # News +## v0.4.11 - dev + +- Add new symbolic bosonic states and operators (`TwoSqueezeOp`, `TwoSqueezedState`, `BosonicThermalState`, `BeamSplitterOp`). + ## v0.4.10 - 2025-05-11 - Polish `Base.show` methods for application products and scaled quantum objects. diff --git a/Project.toml b/Project.toml index ba5d880..377e580 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantumSymbolics" uuid = "efa7fd63-0460-4890-beb7-be1bbdfbaeae" authors = ["QuantumSymbolics.jl contributors"] -version = "0.4.10" +version = "0.4.11-dev" [deps] Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" From a314c98902c92614eb1299bece996c533b5e62e2 Mon Sep 17 00:00:00 2001 From: apkille Date: Mon, 12 May 2025 13:19:24 -0400 Subject: [PATCH 07/12] use curried method of `isequal` in rules.jl --- src/QSymbolicsBase/rules.jl | 55 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 7e796c4..7a4574c 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -11,7 +11,6 @@ function hasscalings(xs) end end _isa(T) = x->isa(x,T) -_isequal(obj) = x->isequal(x, obj) _vecisa(T) = x->all(_isa(T), x) ## @@ -33,36 +32,36 @@ RULES_PAULI = [ @rule(~o1::_isa(HGate)*~o2::_isa(YGate)*~o3::_isa(HGate) => -Y), @rule(~o1::_isa(HGate)*~o2::_isa(ZGate)*~o3::_isa(HGate) => X), - @rule(~o::_isa(XGate)*~k::_isequal(X1) => X1), - @rule(~o::_isa(YGate)*~k::_isequal(X1) => -im*X2), - @rule(~o::_isa(ZGate)*~k::_isequal(X1) => X2), + @rule(~o::_isa(XGate)*~k::isequal(X1) => X1), + @rule(~o::_isa(YGate)*~k::isequal(X1) => -im*X2), + @rule(~o::_isa(ZGate)*~k::isequal(X1) => X2), - @rule(~o::_isa(XGate)*~k::_isequal(X2) => -X2), - @rule(~o::_isa(YGate)*~k::_isequal(X2) => im*X1), - @rule(~o::_isa(ZGate)*~k::_isequal(X2) => X1), + @rule(~o::_isa(XGate)*~k::isequal(X2) => -X2), + @rule(~o::_isa(YGate)*~k::isequal(X2) => im*X1), + @rule(~o::_isa(ZGate)*~k::isequal(X2) => X1), - @rule(~o::_isa(XGate)*~k::_isequal(Y1) => im*Y2), - @rule(~o::_isa(YGate)*~k::_isequal(Y1) => Y1), - @rule(~o::_isa(ZGate)*~k::_isequal(Y1) => Y2), + @rule(~o::_isa(XGate)*~k::isequal(Y1) => im*Y2), + @rule(~o::_isa(YGate)*~k::isequal(Y1) => Y1), + @rule(~o::_isa(ZGate)*~k::isequal(Y1) => Y2), - @rule(~o::_isa(XGate)*~k::_isequal(Y2) => -im*Y1), - @rule(~o::_isa(YGate)*~k::_isequal(Y2) => -Y2), - @rule(~o::_isa(ZGate)*~k::_isequal(Y2) => Y1), + @rule(~o::_isa(XGate)*~k::isequal(Y2) => -im*Y1), + @rule(~o::_isa(YGate)*~k::isequal(Y2) => -Y2), + @rule(~o::_isa(ZGate)*~k::isequal(Y2) => Y1), - @rule(~o::_isa(XGate)*~k::_isequal(Z1) => Z2), - @rule(~o::_isa(YGate)*~k::_isequal(Z1) => im*Z2), - @rule(~o::_isa(ZGate)*~k::_isequal(Z1) => Z1), + @rule(~o::_isa(XGate)*~k::isequal(Z1) => Z2), + @rule(~o::_isa(YGate)*~k::isequal(Z1) => im*Z2), + @rule(~o::_isa(ZGate)*~k::isequal(Z1) => Z1), - @rule(~o::_isa(XGate)*~k::_isequal(Z2) => Z1), - @rule(~o::_isa(YGate)*~k::_isequal(Z2) => -im*Z1), - @rule(~o::_isa(ZGate)*~k::_isequal(Z2) => -Z2), + @rule(~o::_isa(XGate)*~k::isequal(Z2) => Z1), + @rule(~o::_isa(YGate)*~k::isequal(Z2) => -im*Z1), + @rule(~o::_isa(ZGate)*~k::isequal(Z2) => -Z2), - @rule(~o::_isa(HGate)*~k::_isequal(X1) => Z1), - @rule(~o::_isa(HGate)*~k::_isequal(X2) => Z2), - @rule(~o::_isa(HGate)*~k::_isequal(Y1) => (X1+im*X2)/sqrt(2)), - @rule(~o::_isa(HGate)*~k::_isequal(Y2) => (X1-im*X2)/sqrt(2)), - @rule(~o::_isa(HGate)*~k::_isequal(Z1) => X1), - @rule(~o::_isa(HGate)*~k::_isequal(Z2) => X2) + @rule(~o::_isa(HGate)*~k::isequal(X1) => Z1), + @rule(~o::_isa(HGate)*~k::isequal(X2) => Z2), + @rule(~o::_isa(HGate)*~k::isequal(Y1) => (X1+im*X2)/sqrt(2)), + @rule(~o::_isa(HGate)*~k::isequal(Y2) => (X1-im*X2)/sqrt(2)), + @rule(~o::_isa(HGate)*~k::isequal(Z1) => X1), + @rule(~o::_isa(HGate)*~k::isequal(Z2) => X2) ] # Commutator identities @@ -95,7 +94,7 @@ RULES_ANTICOMMUTATOR = [ ] RULES_FOCK = [ - @rule(~o::_isa(DestroyOp) * ~k::_isequal(vac) => SZeroKet()), + @rule(~o::_isa(DestroyOp) * ~k::isequal(vac) => SZeroKet()), @rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => Term(sqrt,[(~k).idx+1])*FockState((~k).idx+1)), @rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => Term(sqrt,[(~k).idx])*FockState((~k).idx-1)), @rule(~o::_isa(NumberOp) * ~k::_isa(FockState) => (~k).idx*(~k)), @@ -106,8 +105,8 @@ RULES_FOCK = [ @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)), @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), @rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)), - @rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis)), - @rule(~o::_isa(TwoSqueezeOp) * ~k::_isequal(vac ⊗ vac) => TwoSqueezedState((~o).z, (~o).basis)) + @rule(~o::_isa(SqueezeOp) * ~k::isequal(vac) => SqueezedState((~o).z, (~o).basis)), + @rule(~o::_isa(TwoSqueezeOp) * ~k::isequal(vac ⊗ vac) => TwoSqueezedState((~o).z, (~o).basis)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] From 3694155082367bd8bd85113fff5f8c34843bca28 Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 4 Jun 2025 09:00:25 -0400 Subject: [PATCH 08/12] simplify wrt basis fields --- src/QSymbolicsBase/predefined_fock.jl | 73 ++++++--------------------- src/QSymbolicsBase/rules.jl | 12 ++--- test/test_throws.jl | 4 -- 3 files changed, 22 insertions(+), 67 deletions(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 22997c9..5e4f523 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -2,48 +2,40 @@ # Predefined objects in the Fock space. ## +const inf_fock_basis = FockBasis(Inf,0) + +abstract type AbstractSingleBosonState <: SpecialKet +abstract type AbstractTwoBosonState <: SpecialKet +basis(::AbstractSingleBosonState) = inf_fock_basis +basis(::AbstractTwoBosonState) = inf_fock_basis^2 + """Fock state in defined Fock basis.""" -@withmetadata struct FockState <: SpecialKet +@withmetadata struct FockState <: AbstractSingleBosonState idx::Int - basis::FockBasis end -FockState(idx::Int) = FockState(idx, inf_fock_basis) symbollabel(x::FockState) = "$(x.idx)" """Coherent state in defined Fock basis.""" -@withmetadata struct CoherentState <: SpecialKet +@withmetadata struct CoherentState <: AbstractSingleBosonState alpha::Number # TODO parameterize - basis::FockBasis end -CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis) symbollabel(x::CoherentState) = "$(x.alpha)" """Squeezed vacuum state in defined Fock basis.""" -@withmetadata struct SqueezedState <: SpecialKet +@withmetadata struct SqueezedState <: AbstractSingleBosonState z::Number - basis::FockBasis end -SqueezedState(z::Number) = SqueezedState(z, inf_fock_basis) symbollabel(x::SqueezedState) = "0,$(x.z)" """Two-mode squeezed vacuum state, or EPR state, in defined Fock basis.""" -@withmetadata struct TwoSqueezedState <: SpecialKet +@withmetadata struct TwoSqueezedState <: AbstractTwoBosonState z::Number - basis::CompositeBasis - function TwoSqueezedState(z::Number, basis::CompositeBasis) - bases = basis.bases - length(bases) == 2 && all(x -> x isa FockBasis, bases) || - throw(ArgumentError(lazy"The underlying basis for an EPR state must be a tensor product of two bases for single-mode quantum systems.")) - return new(z, basis) - end end -TwoSqueezedState(z::Number) = TwoSqueezedState(z, inf_fock_basis^2) symbollabel(x::TwoSqueezedState) = "0,$(x.z)" -const inf_fock_basis = FockBasis(Inf,0.) -"""Vacuum basis state of n""" +"""Vacuum basis state""" const vac = const F₀ = const F0 = FockState(0) -"""Single photon basis state of n""" +"""Single photon basis state""" const F₁ = const F1 = FockState(1) ## @@ -72,10 +64,7 @@ julia> qsimplify(num*f, rewriter=qsimplify_fock) 2|2⟩ ``` """ -@withmetadata struct NumberOp <: AbstractSingleBosonOp - basis::FockBasis -end -NumberOp() = NumberOp(inf_fock_basis) +@withmetadata struct NumberOp <: AbstractSingleBosonOp end symbollabel(::NumberOp) = "n" """Creation (raising) operator. @@ -91,10 +80,7 @@ julia> qsimplify(create*f, rewriter=qsimplify_fock) (sqrt(3))|3⟩ ``` """ -@withmetadata struct CreateOp <: AbstractSingleBosonOp - basis::FockBasis -end -CreateOp() = CreateOp(inf_fock_basis) +@withmetadata struct CreateOp <: AbstractSingleBosonOp end symbollabel(::CreateOp) = "a†" """Annihilation (lowering or destroy) operator in defined Fock basis. @@ -110,10 +96,7 @@ julia> qsimplify(destroy*f, rewriter=qsimplify_fock) (sqrt(2))|1⟩ ``` """ -@withmetadata struct DestroyOp <: AbstractSingleBosonOp - basis::FockBasis -end -DestroyOp() = DestroyOp(inf_fock_basis) +@withmetadata struct DestroyOp <: AbstractSingleBosonOp end symbollabel(::DestroyOp) = "a" """Phase-shift operator in defined Fock basis. @@ -131,9 +114,7 @@ julia> qsimplify(phase*c, rewriter=qsimplify_fock) """ @withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp phase::Number - basis::FockBasis end -PhaseShiftOp(phase::Number) = PhaseShiftOp(phase, inf_fock_basis) symbollabel(x::PhaseShiftOp) = "U($(x.phase))" """Displacement operator in defined Fock basis. @@ -151,9 +132,7 @@ julia> qsimplify(displace*f, rewriter=qsimplify_fock) """ @withmetadata struct DisplaceOp <: AbstractSingleBosonOp alpha::Number - basis::FockBasis end -DisplaceOp(alpha::Number) = DisplaceOp(alpha, inf_fock_basis) symbollabel(x::DisplaceOp) = "D($(x.alpha))" """Number operator, also available as the constant `n̂`, in an infinite dimension Fock basis.""" @@ -176,43 +155,23 @@ julia> qsimplify(S*vac, rewriter=qsimplify_fock) """ @withmetadata struct SqueezeOp <: AbstractSingleBosonOp z::Number - basis::FockBasis end -SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis) symbollabel(x::SqueezeOp) = "S($(x.z))" """Thermal bosonic state in defined Fock basis.""" @withmetadata struct BosonicThermalState <: AbstractSingleBosonOp photons::Number - basis::FockBasis end -BosonicThermalState(photons::Number) = BosonicThermalState(photons, inf_fock_basis) symbollabel(x::BosonicThermalState) = "ρₜₕ($(x.photons))" """Two-mode squeezing operator in defined Fock basis.""" @withmetadata struct TwoSqueezeOp <: AbstractTwoBosonOp z::Number - basis::CompositeBasis - function TwoSqueezeOp(z::Number, basis::CompositeBasis) - bases = basis.bases - length(bases) == 2 && all(x -> x isa FockBasis, bases) || - throw(ArgumentError(lazy"The underlying basis for a two-mode squeeze operator must be a tensor product of two bases for single-mode quantum systems.")) - return new(z, basis) - end end -TwoSqueezeOp(z::Number) = TwoSqueezeOp(z, inf_fock_basis^2) symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))" """Two-mode beamsplitter operator in defined Fock basis.""" @withmetadata struct BeamSplitterOp <: AbstractTwoBosonOp transmit::Number - basis::CompositeBasis - function BeamSplitterOp(transmit::Number, basis::CompositeBasis) - bases = basis.bases - length(bases) == 2 && all(x -> x isa FockBasis, bases) || - throw(ArgumentError(lazy"The underlying basis for a beam splitter operator must be a tensor product of two bases for single-mode quantum systems.")) - return new(transmit, basis) - end end -BeamSplitterOp(transmit::Number) = BeamSplitterOp(transmit, inf_fock_basis^2) symbollabel(x::BeamSplitterOp) = "B($(x.transmit))" \ No newline at end of file diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 7a4574c..1664ffe 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -85,8 +85,8 @@ RULES_ANTICOMMUTATOR = [ @rule(anticommutator(~o1::_isa(YGate), ~o2::_isa(XGate)) => 0), @rule(anticommutator(~o1::_isa(ZGate), ~o2::_isa(YGate)) => 0), @rule(anticommutator(~o1::_isa(XGate), ~o2::_isa(ZGate)) => 0), - @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp((~o1).basis)), - @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp((~o1).basis)), + @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp(basis(~o1))), + @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp(basis(~o1))), @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(DestroyOp)) => -(~o2)), @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(NumberOp)) => (~o1)), @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(CreateOp)) => (~o2)), @@ -102,11 +102,11 @@ RULES_FOCK = [ @rule(~o::_isa(PhaseShiftOp) * ~k::_isa(CoherentState) => CoherentState((~k).alpha * exp(-im*(~o).phase))), @rule(dagger(~o1::_isa(PhaseShiftOp)) * ~o2::_isa(DestroyOp) * ~o1 => ~o2*exp(-im*((~o1).phase))), @rule(~o1::_isa(PhaseShiftOp) * ~o2::_isa(DestroyOp) * dagger(~o1) => ~o2*exp(im*((~o1).phase))), - @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)), - @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), + @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp(basis(~o2))), + @rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp(basis(~o2))), @rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)), - @rule(~o::_isa(SqueezeOp) * ~k::isequal(vac) => SqueezedState((~o).z, (~o).basis)), - @rule(~o::_isa(TwoSqueezeOp) * ~k::isequal(vac ⊗ vac) => TwoSqueezedState((~o).z, (~o).basis)) + @rule(~o::_isa(SqueezeOp) * ~k::isequal(vac) => SqueezedState((~o).z)), + @rule(~o::_isa(TwoSqueezeOp) * ~k::isequal(vac ⊗ vac) => TwoSqueezedState((~o).z)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/test_throws.jl b/test/test_throws.jl index 8e26283..c2f9cc6 100644 --- a/test/test_throws.jl +++ b/test/test_throws.jl @@ -4,7 +4,6 @@ @op A SpinBasis(1//2) ⊗ SpinBasis(1//2); @op B; @op C; @ket k; @bra b; @ket l SpinBasis(1//2) ⊗ SpinBasis(1//2); - fb = FockBasis(10) @test_throws IncompatibleBases A*B @test_throws IncompatibleBases commutator(A, B) @@ -15,7 +14,4 @@ @test_throws IncompatibleBases b*l @test_throws ArgumentError ptrace(B, 2) @test_throws ArgumentError ptrace(B+C, 2) - @test_throws ArgumentError TwoSqueezedState(pi, fb^3) - @test_throws ArgumentError TwoSqueezeOp(pi, fb^3) - @test_throws ArgumentError BeamSplitterOp(pi, fb^3) end \ No newline at end of file From fb53879be39840bde58f31a73a01f4b7adc2fe8c Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 4 Jun 2025 09:05:52 -0400 Subject: [PATCH 09/12] mark unitary operations as gates, not ops --- src/QSymbolicsBase/predefined_fock.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 5e4f523..0c418bc 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -33,9 +33,9 @@ symbollabel(x::SqueezedState) = "0,$(x.z)" end symbollabel(x::TwoSqueezedState) = "0,$(x.z)" -"""Vacuum basis state""" +"""Single-mode vacuum state""" const vac = const F₀ = const F0 = FockState(0) -"""Single photon basis state""" +"""Single photon state""" const F₁ = const F1 = FockState(1) ## @@ -112,7 +112,7 @@ julia> qsimplify(phase*c, rewriter=qsimplify_fock) |1.2246467991473532e-16 - 1.0im⟩ ``` """ -@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp +@withmetadata struct PhaseShiftOp <: AbstractSingleBosonGate phase::Number end symbollabel(x::PhaseShiftOp) = "U($(x.phase))" @@ -130,7 +130,7 @@ julia> qsimplify(displace*f, rewriter=qsimplify_fock) |im⟩ ``` """ -@withmetadata struct DisplaceOp <: AbstractSingleBosonOp +@withmetadata struct DisplaceOp <: AbstractSingleBosonGate alpha::Number end symbollabel(x::DisplaceOp) = "D($(x.alpha))" @@ -153,7 +153,7 @@ julia> qsimplify(S*vac, rewriter=qsimplify_fock) |0,π⟩ ``` """ -@withmetadata struct SqueezeOp <: AbstractSingleBosonOp +@withmetadata struct SqueezeOp <: AbstractSingleBosonGate z::Number end symbollabel(x::SqueezeOp) = "S($(x.z))" @@ -165,13 +165,13 @@ end symbollabel(x::BosonicThermalState) = "ρₜₕ($(x.photons))" """Two-mode squeezing operator in defined Fock basis.""" -@withmetadata struct TwoSqueezeOp <: AbstractTwoBosonOp +@withmetadata struct TwoSqueezeOp <: AbstractTwoBosonGate z::Number end symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))" """Two-mode beamsplitter operator in defined Fock basis.""" -@withmetadata struct BeamSplitterOp <: AbstractTwoBosonOp +@withmetadata struct BeamSplitterOp <: AbstractTwoBosonGate transmit::Number end symbollabel(x::BeamSplitterOp) = "B($(x.transmit))" \ No newline at end of file From 0a23bd0311d95f0416e77535df8a4099b9434279 Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Wed, 4 Jun 2025 10:42:50 -0400 Subject: [PATCH 10/12] fix typing issue --- src/QSymbolicsBase/predefined_fock.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 0c418bc..8af63b7 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -2,7 +2,7 @@ # Predefined objects in the Fock space. ## -const inf_fock_basis = FockBasis(Inf,0) +const inf_fock_basis = FockBasis(Inf,0.0) # Inf is Float, so the second parameter has to be Float too abstract type AbstractSingleBosonState <: SpecialKet abstract type AbstractTwoBosonState <: SpecialKet From a872f78ab0491177264989d657a68e2585875ee2 Mon Sep 17 00:00:00 2001 From: apkille Date: Wed, 4 Jun 2025 14:41:17 -0400 Subject: [PATCH 11/12] fix more typing issues --- src/QSymbolicsBase/predefined_fock.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 8af63b7..c1b256d 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -4,8 +4,8 @@ const inf_fock_basis = FockBasis(Inf,0.0) # Inf is Float, so the second parameter has to be Float too -abstract type AbstractSingleBosonState <: SpecialKet -abstract type AbstractTwoBosonState <: SpecialKet +abstract type AbstractSingleBosonState <: SpecialKet end +abstract type AbstractTwoBosonState <: SpecialKet end basis(::AbstractSingleBosonState) = inf_fock_basis basis(::AbstractTwoBosonState) = inf_fock_basis^2 From 16dff93272a45ac3f07cd92c80354677b43ca22c Mon Sep 17 00:00:00 2001 From: apkille Date: Wed, 4 Jun 2025 14:41:37 -0400 Subject: [PATCH 12/12] rm Fock basis args in test_express_opt.jl --- test/test_express_opt.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_express_opt.jl b/test/test_express_opt.jl index 287ff2a..e4e8f91 100644 --- a/test/test_express_opt.jl +++ b/test/test_express_opt.jl @@ -36,9 +36,9 @@ state = (3im*(2*dagger(Z1)+dagger(Y1))) * (3im*(2*X1+X2)) - cstate = CoherentState(im, inf_fock_basis) - displace = DisplaceOp(im,inf_fock_basis) - phase = PhaseShiftOp(im, inf_fock_basis) + cstate = CoherentState(im) + displace = DisplaceOp(im) + phase = PhaseShiftOp(im) @test express(N*F1) ≈ express(N)*express(F1) @test express(Create*F1) ≈ express(Create)*express(F1) @test express(Destroy*F1) ≈ express(Destroy)*express(F1)