From c41ff6be491cb285015e2bddad98aa740f7e029c Mon Sep 17 00:00:00 2001 From: Andrew Kille <68079167+apkille@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:32:18 -0400 Subject: [PATCH] implement squeezing (#83) * implement squeezing * change `SqueezedCoherentState` to `SqueezedState` * update changelog and project.toml --- CHANGELOG.md | 4 ++++ Project.toml | 2 +- docs/src/QHO.md | 25 +++++++++++++++------- ext/QuantumOpticsExt/QuantumOpticsExt.jl | 2 ++ src/QSymbolicsBase/QSymbolicsBase.jl | 4 ++-- src/QSymbolicsBase/predefined_fock.jl | 27 +++++++++++++++++++++++- src/QSymbolicsBase/rules.jl | 3 ++- test/test_express_opt.jl | 4 ++++ test/test_fock.jl | 6 ++++++ 9 files changed, 64 insertions(+), 13 deletions(-) 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" diff --git a/docs/src/QHO.md b/docs/src/QHO.md index 764e9eb..a218b6c 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 vacuum state: `SqueezedState(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..fff1fd5 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::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)) 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..d707c49 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,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, qexpand, diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index 5434194..b3294d5 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -18,6 +18,14 @@ 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 + z::Number + basis::FockBasis +end +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""" const vac = const F₀ = const F0 = FockState(0) @@ -136,4 +144,21 @@ 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> S = SqueezeOp(pi) +S(π) + +julia> qsimplify(S*vac, rewriter=qsimplify_fock) +|0,π⟩ +``` +""" +@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..a0a9402 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::_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 97753ee..287ff2a 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) + + 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 9ea691b..2660896 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) + sstate = SqueezedState(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*vac, rewriter=qsimplify_fock), sstate) + end end \ No newline at end of file