From 57faf25323aab4ae71207b8f2c95ea3ba85e2dc8 Mon Sep 17 00:00:00 2001 From: apkille Date: Sat, 24 Aug 2024 19:50:35 -0400 Subject: [PATCH 1/3] implement squeezing --- docs/src/QHO.md | 25 +++++++++++++------ ext/QuantumOpticsExt/QuantumOpticsExt.jl | 2 ++ src/QSymbolicsBase/QSymbolicsBase.jl | 4 +-- src/QSymbolicsBase/predefined_fock.jl | 31 +++++++++++++++++++++++- src/QSymbolicsBase/rules.jl | 3 ++- test/test_express_opt.jl | 4 +++ test/test_fock.jl | 6 +++++ 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/docs/src/QHO.md b/docs/src/QHO.md index 764e9eb..0e1cdd8 100644 --- a/docs/src/QHO.md +++ b/docs/src/QHO.md @@ -10,7 +10,7 @@ In this section, we describe symbolic representations of bosonic systems in Quan ## States -A Fock state is a state with well defined number of excitation quanta of a single quantum harmonic oscillator (an eigenstate of the number operator). In the following example, we create a `FockState` with 3 quanta in an infinite-dimension Fock space: +A Fock state is a state with well defined number of excitation quanta of a single quantum harmonic oscillator (an eigenstate of the number operator). In the following example, we create a [`FockState`](@ref) with 3 quanta in an infinite-dimension Fock space: ```jldoctest julia> f = FockState(3) @@ -22,7 +22,7 @@ Both vacuum (ground) and single-photon states are defined as constants in both u - `vac = F₀ = F0` $=|0\rangle$ in the number state representation, - `F₁ = F1` $=|1\rangle$ in the number state representation. -To create quantum analogues of a classical harmonic oscillator, or monochromatic electromagnetic waves, we can define a coherent (a.k.a. semi-classical) state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with `CoherentState(α::Number)`: +To create quantum analogues of a classical harmonic oscillator, or monochromatic electromagnetic waves, we can define a coherent (a.k.a. semi-classical) state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with [`CoherentState`](@ref): ```jldoctest julia> c = CoherentState(im) @@ -31,9 +31,15 @@ julia> c = CoherentState(im) !!! note "Naming convention for quantum harmonic oscillator bases" The defined basis for arbitrary symbolic bosonic states is a `FockBasis` object, due to a shared naming interface for Quantum physics packages. For instance, the command `basis(CoherentState(im))` will output `Fock(cutoff=Inf)`. This may lead to confusion, as not all bosonic states are Fock states. However, this is simply a naming convention for the basis, and symbolic and numerical results are not affected by it. +Summarized below are supported bosonic states. + +- Fock state: `FockState(idx::Int)`, +- Coherent state: `CoherentState(alpha::Number)`, +- Squeezed coherent state: `SqueezedCoherentState(alpha::Number, z::Number)`. + ## Operators -Operations on bosonic states are supported, and can be simplified with `qsimplify` and its rewriter `qsimplify_fock`. For instance, we can apply the raising (creation) $\hat{a}^{\dagger}$ and lowering (annihilation or destroy) $\hat{a}$ operators on a Fock state as follows: +Operations on bosonic states are supported, and can be simplified with [`qsimplify`](@ref) and its rewriter `qsimplify_fock`. For instance, we can apply the raising (creation) $\hat{a}^{\dagger}$ and lowering (annihilation or destroy) $\hat{a}$ operators on a Fock state as follows: ```jldoctest julia> f = FockState(3); @@ -68,8 +74,10 @@ Constants are defined for number and ladder operators in unicode and ASCII: - `Create = âꜛ` $=\hat{a}^{\dagger}$, - `Destroy = â` $=\hat{a}$. -Phase-shift $U(\theta)$ and displacement $D(\alpha)$ operators, defined respectively as +Phase-shift $U(\theta)$ and displacement $D(\alpha)$ operators, defined respectively as + $$U(\theta) = \exp\left(-i\theta\hat{n}\right) \quad \text{and} \quad D(\alpha) = \exp\left(\alpha\hat{a}^{\dagger} - \alpha\hat{a}\right),$$ + can be defined with usual simplification rules. Consider the following example: ```jldoctest @@ -85,7 +93,7 @@ U(π) julia> qsimplify(phase*c, rewriter=qsimplify_fock) |1.2246467991473532e-16 - 1.0im⟩ ``` -Here, we generated a coherent state $|i\rangle$ from the vacuum state $|0\rangle$ by applying the displacement operator defined by `DisplaceOp`. Then, we shifted its phase by $\pi$ with the phase shift operator (which is called with `PhaseShiftOp`) to get the result $|-i\rangle$. +Here, we generated a coherent state $|i\rangle$ from the vacuum state $|0\rangle$ by applying the displacement operator defined by [`DisplaceOp`](@ref). Then, we shifted its phase by $\pi$ with the phase shift operator (which is called with [`PhaseShiftOp`](@ref)) to get the result $|-i\rangle$. Summarized below are supported bosonic operators. @@ -93,11 +101,12 @@ Summarized below are supported bosonic operators. - Creation operator: `CreateOp()`, - Annihilation operator: `DestroyOp()`, - Phase-shift operator: `PhaseShiftOp(phase::Number)`, -- Displacement operator: `DisplaceOp(alpha::Number)`. +- Displacement operator: `DisplaceOp(alpha::Number)`, +- Squeezing operator: `SqueezeOp(z::Number)`. ## Numerical Conversions to QuantumOptics.jl -Bosonic systems can be translated to the ket representation with `express`. For instance: +Bosonic systems can be translated to the ket representation with [`express`](@ref). For instance: ```jldoctest julia> f = FockState(1); @@ -132,7 +141,7 @@ Ket(dim=3) ``` !!! warning "Cutoff specifications for numerical representations of quantum harmonic oscillators" - Symbolic bosonic states and operators are naturally represented in an infinite dimension basis. For numerical conversions of such quantum objects, a finite cutoff of the highest allowed state must be defined. By default, the basis dimension of numerical conversions is set to 3 (so the number representation cutoff is 2), as demonstrated above. To define a different cutoff, one must customize the `QuantumOpticsRepr` instance, e.g. provide `QuantumOpticsRepr(cutoff=n::Int)` to `express`. + Symbolic bosonic states and operators are naturally represented in an infinite dimension basis. For numerical conversions of such quantum objects, a finite cutoff of the highest allowed state must be defined. By default, the basis dimension of numerical conversions is set to 3 (so the number representation cutoff is 2), as demonstrated above. To define a different cutoff, one must customize the `QuantumOpticsRepr` instance, e.g. provide `QuantumOpticsRepr(cutoff=n::Int)` to [`express`](@ref). If we wish to specify a different numerical cutoff, say 4, to the previous examples, then we rewrite them as follows: diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index 81ca2fa..bd9064e 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -84,10 +84,12 @@ function finite_basis(s,r) end express_nolookup(s::FockState, r::QuantumOpticsRepr) = fockstate(finite_basis(s,r),s.idx) express_nolookup(s::CoherentState, r::QuantumOpticsRepr) = coherentstate(finite_basis(s,r),s.alpha) +express_nolookup(s::SqueezedCoherentState, r::QuantumOpticsRepr) = (b = finite_basis(s,r); squeeze(b, s.z)*coherentstate(b, s.alpha)) express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(finite_basis(o,r)) express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(finite_basis(o,r)) express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(finite_basis(o,r)) express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) = displace(finite_basis(o,r), o.alpha) +express_nolookup(o::SqueezeOp, r::QuantumOpticsRepr) = squeeze(finite_basis(o,r), o.z) express_nolookup(x::MixedState, r::QuantumOpticsRepr) = identityoperator(finite_basis(x,r))/length(finite_basis(x,r)) # TODO there is probably a more efficient way to represent it express_nolookup(x::IdentityOp, r::QuantumOpticsRepr) = identityoperator(finite_basis(x,r)) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 8825cfe..2f4f78c 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -41,8 +41,8 @@ export SymQObj,QObj, MixedState,IdentityOp, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, - XBasisState,YBasisState,ZBasisState,FockState,CoherentState, - NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp, + XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedCoherentState, + NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp, 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 5434194..41697a8 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -18,6 +18,15 @@ end CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis) symbollabel(x::CoherentState) = "$(x.alpha)" +"""Squeezed coherent state in defined Fock basis.""" +@withmetadata struct SqueezedCoherentState <: SpecialKet + alpha::Number + z::Number + basis::FockBasis +end +SqueezedCoherentState(alpha::Number, z::Number) = SqueezedCoherentState(alpha, z, inf_fock_basis) +symbollabel(x::SqueezedCoherentState) = "$(x.alpha),$(x.z)" + const inf_fock_basis = FockBasis(Inf,0.) """Vacuum basis state of n""" const vac = const F₀ = const F0 = FockState(0) @@ -136,4 +145,24 @@ const N = const n̂ = NumberOp() There is no unicode dagger superscript, so we use the uparrow""" const Create = const âꜛ = CreateOp() """Annihilation operator, also available as the constant `â`, in an infinite dimension Fock basis.""" -const Destroy = const â = DestroyOp() \ No newline at end of file +const Destroy = const â = DestroyOp() + +"""Squeezing operator in defined Fock basis. + +```jldoctest +julia> c = CoherentState(im) +|im⟩ + +julia> S = SqueezeOp(pi) +S(π) + +julia> qsimplify(S*c, rewriter=qsimplify_fock) +|im,π⟩ +``` +""" +@withmetadata struct SqueezeOp <: AbstractSingleBosonOp + z::Number + basis::FockBasis +end +SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis) +symbollabel(x::SqueezeOp) = "S($(x.z))" \ No newline at end of file diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index ac31f95..7297b62 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -105,7 +105,8 @@ RULES_FOCK = [ @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(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)) + @rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)), + @rule(~o::_isa(SqueezeOp) * ~k::_isa(CoherentState) => SqueezedCoherentState((~k).alpha, (~o).z)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/test_express_opt.jl b/test/test_express_opt.jl index 845148d..13fcc03 100644 --- a/test/test_express_opt.jl +++ b/test/test_express_opt.jl @@ -43,4 +43,8 @@ @test express(Create*F1) ≈ express(Create)*express(F1) @test express(Destroy*F1) ≈ express(Destroy)*express(F1) @test express(displace*cstate) ≈ express(displace)*express(cstate) + + squeezedcstate = SqueezedCoherentState(im, pi) + squeezeop = SqueezeOp(pi) + @test express(squeezedcstate) ≈ express(squeezeop)*express(cstate) end diff --git a/test/test_fock.jl b/test/test_fock.jl index 9ea691b..f8e92e3 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -6,6 +6,8 @@ phase1 = PhaseShiftOp(0) phase2 = PhaseShiftOp(pi) displace = DisplaceOp(im) + squeezeop = SqueezeOp(pi) + scstate = SqueezedCoherentState(im,pi) @testset "ladder and number operators" begin @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) @@ -23,4 +25,8 @@ @test isequal(qsimplify(dagger(displace)*Create*displace, rewriter=qsimplify_fock), Create - im*IdentityOp(inf_fock_basis)) @test isequal(qsimplify(displace*vac, rewriter=qsimplify_fock), cstate) end + + @testset "Squeeze operators" begin + @test isequal(qsimplify(squeezeop*cstate, rewriter=qsimplify_fock), scstate) + end end \ No newline at end of file From 57c8e350c6b2deae58a9d980a8bea823db2d65fe Mon Sep 17 00:00:00 2001 From: apkille Date: Mon, 16 Sep 2024 20:49:17 -0400 Subject: [PATCH 2/3] change `SqueezedCoherentState` to `SqueezedState` --- docs/src/QHO.md | 2 +- ext/QuantumOpticsExt/QuantumOpticsExt.jl | 2 +- src/QSymbolicsBase/QSymbolicsBase.jl | 2 +- src/QSymbolicsBase/predefined_fock.jl | 16 ++++++---------- src/QSymbolicsBase/rules.jl | 2 +- test/test_express_opt.jl | 6 +++--- test/test_fock.jl | 4 ++-- 7 files changed, 15 insertions(+), 19 deletions(-) diff --git a/docs/src/QHO.md b/docs/src/QHO.md index 0e1cdd8..a218b6c 100644 --- a/docs/src/QHO.md +++ b/docs/src/QHO.md @@ -35,7 +35,7 @@ Summarized below are supported bosonic states. - Fock state: `FockState(idx::Int)`, - Coherent state: `CoherentState(alpha::Number)`, -- Squeezed coherent state: `SqueezedCoherentState(alpha::Number, z::Number)`. +- Squeezed vacuum state: `SqueezedState(z::Number)`. ## Operators diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index bd9064e..fff1fd5 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -84,7 +84,7 @@ function finite_basis(s,r) end express_nolookup(s::FockState, r::QuantumOpticsRepr) = fockstate(finite_basis(s,r),s.idx) express_nolookup(s::CoherentState, r::QuantumOpticsRepr) = coherentstate(finite_basis(s,r),s.alpha) -express_nolookup(s::SqueezedCoherentState, r::QuantumOpticsRepr) = (b = finite_basis(s,r); squeeze(b, s.z)*coherentstate(b, s.alpha)) +express_nolookup(s::SqueezedState, r::QuantumOpticsRepr) = (b = finite_basis(s,r); squeeze(b, s.z)*fockstate(b, 0)) express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(finite_basis(o,r)) express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(finite_basis(o,r)) express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(finite_basis(o,r)) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 2f4f78c..d707c49 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -41,7 +41,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,SqueezedCoherentState, + XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState, NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock, diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 41697a8..b3294d5 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -18,14 +18,13 @@ end CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis) symbollabel(x::CoherentState) = "$(x.alpha)" -"""Squeezed coherent state in defined Fock basis.""" -@withmetadata struct SqueezedCoherentState <: SpecialKet - alpha::Number +"""Squeezed vacuum state in defined Fock basis.""" +@withmetadata struct SqueezedState <: SpecialKet z::Number basis::FockBasis end -SqueezedCoherentState(alpha::Number, z::Number) = SqueezedCoherentState(alpha, z, inf_fock_basis) -symbollabel(x::SqueezedCoherentState) = "$(x.alpha),$(x.z)" +SqueezedState(z::Number) = SqueezedState(z, inf_fock_basis) +symbollabel(x::SqueezedState) = "0,$(x.z)" const inf_fock_basis = FockBasis(Inf,0.) """Vacuum basis state of n""" @@ -150,14 +149,11 @@ const Destroy = const â = DestroyOp() """Squeezing operator in defined Fock basis. ```jldoctest -julia> c = CoherentState(im) -|im⟩ - julia> S = SqueezeOp(pi) S(π) -julia> qsimplify(S*c, rewriter=qsimplify_fock) -|im,π⟩ +julia> qsimplify(S*vac, rewriter=qsimplify_fock) +|0,π⟩ ``` """ @withmetadata struct SqueezeOp <: AbstractSingleBosonOp diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 7297b62..a0a9402 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -106,7 +106,7 @@ 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::_isa(CoherentState) => SqueezedCoherentState((~k).alpha, (~o).z)) + @rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/test_express_opt.jl b/test/test_express_opt.jl index ae153c3..287ff2a 100644 --- a/test/test_express_opt.jl +++ b/test/test_express_opt.jl @@ -44,7 +44,7 @@ @test express(Destroy*F1) ≈ express(Destroy)*express(F1) @test express(displace*cstate) ≈ express(displace)*express(cstate) - squeezedcstate = SqueezedCoherentState(im, pi) - squeezeop = SqueezeOp(pi) - @test express(squeezedcstate) ≈ express(squeezeop)*express(cstate) + squeezed = SqueezedState(pi/4) + squeezeop = SqueezeOp(pi/4) + @test express(squeezed) ≈ express(squeezeop)*express(vac) end diff --git a/test/test_fock.jl b/test/test_fock.jl index f8e92e3..2660896 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -7,7 +7,7 @@ phase2 = PhaseShiftOp(pi) displace = DisplaceOp(im) squeezeop = SqueezeOp(pi) - scstate = SqueezedCoherentState(im,pi) + sstate = SqueezedState(pi) @testset "ladder and number operators" begin @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) @@ -27,6 +27,6 @@ end @testset "Squeeze operators" begin - @test isequal(qsimplify(squeezeop*cstate, rewriter=qsimplify_fock), scstate) + @test isequal(qsimplify(squeezeop*vac, rewriter=qsimplify_fock), sstate) end end \ No newline at end of file From d8f8964deef4d1c99eb881095320d925add8f759 Mon Sep 17 00:00:00 2001 From: apkille Date: Mon, 16 Sep 2024 21:24:53 -0400 Subject: [PATCH 3/3] update changelog and project.toml --- CHANGELOG.md | 4 ++++ Project.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cccf8e..a0accc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # News +## v0.4.4 - 2024-09-16 + +- Implement squeezing with `SqueezeOp` and `SqueezedState`. + ## v0.4.3 - 2024-08-13 - **(fix)** Fix for incorrect basis for `express(_,::QuantumOpticsRepr)` for certain operators. diff --git a/Project.toml b/Project.toml index 76f73a4..fd0be53 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.3" +version = "0.4.4" [deps] Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"