From e0e26349dc6407444d94111cd653ea7064e4c875 Mon Sep 17 00:00:00 2001 From: apkille Date: Mon, 8 Jul 2024 16:59:58 -0400 Subject: [PATCH 01/11] fock rules --- src/QSymbolicsBase/QSymbolicsBase.jl | 2 +- src/QSymbolicsBase/predefined.jl | 27 ++++++++++++++++----------- src/QSymbolicsBase/rules.jl | 17 +++++++++++++++-- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 796cc75..af7ab3d 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -38,7 +38,7 @@ export SymQObj,QObj, SProjector,MixedState,IdentityOp,SInvOperator, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, - XBasisState,YBasisState,ZBasisState, + XBasisState,YBasisState,ZBasisState,FockState, NumberOp,CreateOp,DestroyOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator, diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index 7e599cc..f10bc99 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -25,11 +25,11 @@ symbollabel(x::YBasisState) = "Y$(num_to_sub(x.idx))" end symbollabel(x::ZBasisState) = "Z$(num_to_sub(x.idx))" -@withmetadata struct FockBasisState <: SpecialKet +@withmetadata struct FockState <: SpecialKet idx::Int basis::Basis end -symbollabel(x::FockBasisState) = "$(x.idx)" +symbollabel(x::FockState) = "$(x.idx)" @withmetadata struct DiscreteCoherentState <: SpecialKet alpha::Number # TODO parameterize @@ -71,10 +71,9 @@ const Z2 = const Z₂ = const L1 = const L₁ = ZBasisState(2, qubit_basis) const inf_fock_basis = FockBasis(Inf,0.) """Vacuum basis state of n""" -const vac = const F₀ = const F0 = FockBasisState(0,inf_fock_basis) +const vac = const F₀ = const F0 = FockState(0,inf_fock_basis) """Single photon basis state of n""" -const F₁ = const F1 = FockBasisState(1,inf_fock_basis) - +const F₁ = const F1 = FockState(1,inf_fock_basis) ## # Gates and Operators on qubits @@ -182,19 +181,25 @@ abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe isexpr(::AbstractSingleBosonGate) = false basis(::AbstractSingleBosonGate) = inf_fock_basis -@withmetadata struct NumberOp <: AbstractSingleBosonOp end +@withmetadata struct NumberOp <: AbstractSingleBosonOp + basis::Basis +end symbollabel(::NumberOp) = "n" -@withmetadata struct CreateOp <: AbstractSingleBosonOp end +@withmetadata struct CreateOp <: AbstractSingleBosonOp + basis::Basis +end symbollabel(::CreateOp) = "a†" -@withmetadata struct DestroyOp <: AbstractSingleBosonOp end +@withmetadata struct DestroyOp <: AbstractSingleBosonOp + basis::Basis +end symbollabel(::DestroyOp) = "a" """Number operator, also available as the constant `n̂`""" -const N = const n̂ = NumberOp() +const N = const n̂ = NumberOp(inf_fock_basis) """Creation operator, also available as the constant `âꜛ` - there is no unicode dagger superscript, so we use the uparrow""" -const Create = const âꜛ = CreateOp() +const Create = const âꜛ = CreateOp(inf_fock_basis) """Annihilation operator, also available as the constant `â`""" -const Destroy = const â = DestroyOp() +const Destroy = const â = DestroyOp(inf_fock_basis) ## # Other special or useful objects diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 38db184..6c5f605 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -53,10 +53,23 @@ RULES_ANTICOMMUTATOR = [ @rule(anticommutator(~o1::_isa(ZGate), ~o2::_isa(XGate)) => 0), @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(anticommutator(~o1::_isa(XGate), ~o2::_isa(ZGate)) => 0), + @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp(inf_fock_basis)), + @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp(inf_fock_basis)), + @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(DestroyOp)) => -Destroy), + @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(NumberOp)) => Destroy), + @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(CreateOp)) => Create), + @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(NumberOp)) => -Create) ] -RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR] +RULES_FOCK = [ + @rule(~o::_isa(DestroyOp) * ~f::(x->isequal(x, vac)) => SZeroKet()), + @rule(~o::_isa(CreateOp) * ~f::_isa(FockState) => sqrt((~f).idx+1)*FockState((~f).idx+1, inf_fock_basis)), + @rule(~o::_isa(DestroyOp) * ~f::_isa(FockState) => sqrt((~f).idx)*FockState((~f).idx-1, inf_fock_basis)), + @rule(~o::_isa(NumberOp) * ~f::_isa(FockState) => (~f).idx*(~f)) +] + +RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] ## # Simplification rewriters From 269b15f7625c52466faa8db8e7f15153cd3923fb Mon Sep 17 00:00:00 2001 From: apkille Date: Tue, 9 Jul 2024 11:05:42 -0400 Subject: [PATCH 02/11] Fock ops --- ext/QuantumOpticsExt/QuantumOpticsExt.jl | 4 ++-- src/QSymbolicsBase/QSymbolicsBase.jl | 6 ++--- src/QSymbolicsBase/predefined.jl | 10 +++++++++ src/QSymbolicsBase/rules.jl | 28 +++++++++++++++--------- test/test_fock.jl | 10 +++++++++ 5 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 test/test_fock.jl diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index ec0188d..cf43284 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -7,7 +7,7 @@ using QuantumSymbolics: XCXGate, XCYGate, XCZGate, YCXGate, YCYGate, YCZGate, ZCXGate, ZCYGate, ZCZGate, XBasisState, YBasisState, ZBasisState, NumberOp, CreateOp, DestroyOp, - FockBasisState, + FockState, MixedState, IdentityOp, qubit_basis, inf_fock_basis import QuantumSymbolics: express, express_nolookup @@ -70,7 +70,7 @@ express_nolookup(s::XBasisState, ::QuantumOpticsRepr) = (_s₊,_s₋)[s.idx] express_nolookup(s::YBasisState, ::QuantumOpticsRepr) = (_i₊,_i₋)[s.idx] express_nolookup(s::ZBasisState, ::QuantumOpticsRepr) = (_l0,_l1)[s.idx] -function express_nolookup(o::FockBasisState, r::QuantumOpticsRepr) +function express_nolookup(o::FockState, r::QuantumOpticsRepr) @warn "Fock space cutoff is not specified so we default to 2" @assert o.idx<2 "without a specified cutoff you can not create states higher than 1 photon" return (_f0₂,_f1₂)[o.idx+1] diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index af7ab3d..5619db9 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -39,9 +39,9 @@ export SymQObj,QObj, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, XBasisState,YBasisState,ZBasisState,FockState, - NumberOp,CreateOp,DestroyOp, + NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplacementOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, - qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator, + qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock, qexpand, isunitary @@ -141,7 +141,7 @@ function Base.isequal(x::X,y::Y) where {X<:SymQObj, Y<:SymQObj} end Base.isequal(::SymQObj, ::Symbolic{Complex}) = false Base.isequal(::Symbolic{Complex}, ::SymQObj) = false - +Base.isequal(x::Metadata, y::Metadata) = isequal(x.express_cache,y.express_cache) # TODO check that this does not cause incredibly bad runtime performance # use a macro to provide specializations if that is indeed the case diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index f10bc99..33ebd37 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -193,6 +193,16 @@ symbollabel(::CreateOp) = "a†" basis::Basis end symbollabel(::DestroyOp) = "a" +@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp + phase::Number + basis::Basis +end +symbollabel(x::PhaseShiftOp) = "Û($(x.phase))" +@withmetadata struct DisplacementOp <: AbstractSingleBosonOp + alpha::Number + basis::Basis +end +symbollabel(x::DisplacementOp) = "D̂($(x.alpha))" """Number operator, also available as the constant `n̂`""" const N = const n̂ = NumberOp(inf_fock_basis) diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 6c5f605..6144c27 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -54,19 +54,26 @@ 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(inf_fock_basis)), - @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp(inf_fock_basis)), - @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(DestroyOp)) => -Destroy), - @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(NumberOp)) => Destroy), - @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(CreateOp)) => Create), - @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(NumberOp)) => -Create) + @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(NumberOp), ~o2::_isa(DestroyOp)) => -(~o2)), + @rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(NumberOp)) => (~o1)), + @rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(CreateOp)) => (~o2)), + @rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(NumberOp)) => -(~o1)) ] RULES_FOCK = [ - @rule(~o::_isa(DestroyOp) * ~f::(x->isequal(x, vac)) => SZeroKet()), - @rule(~o::_isa(CreateOp) * ~f::_isa(FockState) => sqrt((~f).idx+1)*FockState((~f).idx+1, inf_fock_basis)), - @rule(~o::_isa(DestroyOp) * ~f::_isa(FockState) => sqrt((~f).idx)*FockState((~f).idx-1, inf_fock_basis)), - @rule(~o::_isa(NumberOp) * ~f::_isa(FockState) => (~f).idx*(~f)) + @rule(~o::_isa(DestroyOp) * ~k::(x->isequal(x, vac)) => SZeroKet()), + @rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => sqrt((~k).idx+1)*FockState((~k).idx+1, (~k).basis)), + @rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => sqrt((~k).idx)*FockState((~k).idx-1, (~k).basis)), + @rule(~o::_isa(NumberOp) * ~k::_isa(FockState) => (~k).idx*(~k)), + @rule(~o::_isa(DestroyOp) * ~k::_isa(ContinuousCoherentState) => (~k).idx*(~k)), + @rule(~o::_isa(PhaseShiftOp) * ~k::_isa(ContinuousCoherentState) => ContinuousCoherentState((~k).alpha * exp(-im*((~o).phase)), (~k).basis)), + @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(DisplacementOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)), + @rule(dagger(~o1::_isa(DisplacementOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), + @rule(~o::_isa(DisplacementOp) * ~k::(x->isequal(x, vac)) => ContinuousCoherentState((~o).alpha, (~k).basis)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] @@ -78,6 +85,7 @@ RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOC qsimplify_anticommutator = Chain(RULES_ANTICOMMUTATOR) qsimplify_pauli = Chain(RULES_PAULI) qsimplify_commutator = Chain(RULES_COMMUTATOR) +qsimplify_fock = Chain(RULES_FOCK) """Manually simplify a symbolic expression of quantum objects. diff --git a/test/test_fock.jl b/test/test_fock.jl new file mode 100644 index 0000000..e8b9eb4 --- /dev/null +++ b/test/test_fock.jl @@ -0,0 +1,10 @@ +using QuantumSymbolics +using QuantumSymbolics: inf_fock_basis +using Test + +state1 = FockState(1, inf_fock_basis) +state2 = FockState(2, inf_fock_basis) + +@testset "ladder operators" begin + @test isequal(qsimplify(Create*state1, rewriter=qsimplify_fock), sqrt(2)*state2) +end \ No newline at end of file From 378d308e39d914d9b1f46d6f75feb95ab45948b4 Mon Sep 17 00:00:00 2001 From: apkille Date: Wed, 10 Jul 2024 15:44:54 -0400 Subject: [PATCH 03/11] docs and tests --- docs/src/fock.md | 147 +++++++++++++++++++++++ ext/QuantumOpticsExt/QuantumOpticsExt.jl | 24 +--- src/QSymbolicsBase/QSymbolicsBase.jl | 2 +- src/QSymbolicsBase/predefined.jl | 5 +- src/QSymbolicsBase/rules.jl | 8 +- test/runtests.jl | 1 + test/test_fock.jl | 19 ++- 7 files changed, 180 insertions(+), 26 deletions(-) create mode 100644 docs/src/fock.md diff --git a/docs/src/fock.md b/docs/src/fock.md new file mode 100644 index 0000000..3bbd3b0 --- /dev/null +++ b/docs/src/fock.md @@ -0,0 +1,147 @@ +# Fock States + +```@meta +DocTestSetup = quote + using QuantumSymbolics, QuantumOptics +end +``` + +In this section, we describe symbolic representations of Fock states in QuantumSymbolics, which can be numerically translated to `QuantumOptics.jl`. A Fock state is the number state representation $|n\rangle$ of a system with $n$ particles. + +One can define a basis of the Fock space with `FockBasis(N, offset=0)`, where `N` is the highest available Fock state and `offset` is the lowest cutoff state, which is by default zero. In the following example, we create a `FockState` with 3 quanta in an infinite-dimension Fock space: + +```jldoctest +julia> b = FockBasis(Inf, 0.0) +Fock(cutoff=Inf) + +julia> f = FockState(3, b) +|3⟩ +``` + +Both vacuum (ground) and single-photon states in an infinite-dimension Fock basis are defined as constants in both unicode and ASCII for convenience: + +- `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 state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with `ContinuousCoherentState(α::Number, basis::Basis)`: + +```jldoctest +julia> b = FockBasis(Inf, 0.0); + +julia> c = ContinuousCoherentState(im, b) +|0 + im⟩ +``` + +## Operators + +Operations on fock states are supported, and can be simplified with `qsimplify` and its rewriter `qsimplify_fock`. For instance, we can apply the raising (creation) $a^{\dagger}$ and lowering (annihilation or destroy) $a$ operators on a fock state as follows: + +```jldoctest +julia> b = FockBasis(Inf, 0.0); + +julia> f = FockState(3, b); + +julia> raise = Create*f +a†|3⟩ + +julia> qsimplify(raise, rewriter=qsimplify_fock) +2.0|4⟩ + +julia> lower = Destroy*f +a|3⟩ + +julia> qsimplify(lower, rewriter=qsimplify_fock) +1.7320508075688772|2⟩ +``` +Or, we can apply the number operator $\hat{n}$ to our fock state: + +```jldoctest +julia> b = FockBasis(Inf, 0.0); + +julia> f = FockState(3, b); + +julia> num = N*f +n|3⟩ + +julia> qsimplify(num, rewirter=qsimplify_fock) +3|3⟩ +``` + +In the infinite dimension case for Fock states, constants are defined for number and ladder operators: + +- `N = n̂` $=\hat{n}$, +- `Create = âꜛ` $=\hat{a}^{\dagger}$, +- `Destroy = â` $=\hat{a}$. + +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. Consider the following example: + +```jldoctest +julia> b = FockBasis(Inf, 0.0); + +julia> displace = DisplacementOp(im, b) +D(im) + +julia> c = qsimplify(displace*v, rewriter=qsimplify_fock) +|im⟩ + +julia> phase = PhaseShiftOp(pi, b) +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 `DisplacementOp`. Then, we shifted its phase by $\pi$ with the phase shift operator (which is called with `PhaseShiftOp`) to get the result $|-i\rangle$. + +Summarized below are supported operators, which can be defined in any Fock basis. + +- Number operator: `NumberOp(basis:Basis)`, +- Creation operator: `CreateOp(basis::Basis)`, +- Annihilation operator: `DestroyOp(basis::Basis)`, +- Phase-shift operator: `PhaseShiftOp(phase::Number, basis:Basis)`, +- Displacement operator: `DisplacementOp(alpha::Number, basis::Basis)`. + +## Numerical Conversions to QuantumOptics.jl + +Fock systems can be translated to the ket representation with `express`. For instance: + +```jldoctest +julia> using QuantumOptics + +julia> b = FockBasis(3); + +julia> f = FockState(1, b); + +julia> express(f) +Ket(dim=4) + basis: Fock(cutoff=3) + 0.0 + 0.0im + 1.0 + 0.0im + 0.0 + 0.0im + 0.0 + 0.0im + +julia> julia> express(CreateOp(b)) |> dense +Operator(dim=4x4) + basis: Fock(cutoff=3) + 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 1.41421+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 1.73205+0.0im 0.0+0.0im + +julia> express(CreateOp(b)*f) +Ket(dim=4) + basis: Fock(cutoff=3) + 0.0 + 0.0im + 0.0 + 0.0im + 1.4142135623730951 + 0.0im + 0.0 + 0.0im + +julia> express(DestroyOp(b)*f) +Ket(dim=4) + basis: Fock(cutoff=3) + 1.0 + 0.0im + 0.0 + 0.0im + 0.0 + 0.0im + 0.0 + 0.0im +``` \ No newline at end of file diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index cf43284..8dc1898 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -70,24 +70,12 @@ express_nolookup(s::XBasisState, ::QuantumOpticsRepr) = (_s₊,_s₋)[s.idx] express_nolookup(s::YBasisState, ::QuantumOpticsRepr) = (_i₊,_i₋)[s.idx] express_nolookup(s::ZBasisState, ::QuantumOpticsRepr) = (_l0,_l1)[s.idx] -function express_nolookup(o::FockState, r::QuantumOpticsRepr) - @warn "Fock space cutoff is not specified so we default to 2" - @assert o.idx<2 "without a specified cutoff you can not create states higher than 1 photon" - return (_f0₂,_f1₂)[o.idx+1] -end -function express_nolookup(o::NumberOp, r::QuantumOpticsRepr) - @warn "Fock space cutoff is not specified so we default to 2" - return _n₂ -end -function express_nolookup(o::CreateOp, r::QuantumOpticsRepr) - @warn "Fock space cutoff is not specified so we default to 2" - return _ad₂ -end -function express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) - @warn "Fock space cutoff is not specified so we default to 2" - return _a₂ -end - +express_nolookup(o::FockState, r::QuantumOpticsRepr) = fockstate(o.basis, o.idx) +express_nolookup(o::ContinuousCoherentState, r::QuantumOpticsRepr) = coherentstate(o.basis, o.alpha) +express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(o.basis) +express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(o.basis) +express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(o.basis) +express_nolookup(o::DisplacementOp, r::QuantumOpticsRepr) = displace(o.basis, o.alpha) express_nolookup(x::MixedState, ::QuantumOpticsRepr) = identityoperator(basis(x))/length(basis(x)) # TODO there is probably a more efficient way to represent it function express_nolookup(x::IdentityOp, ::QuantumOpticsRepr) b = basis(x) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 5619db9..e8cbca8 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -38,7 +38,7 @@ export SymQObj,QObj, SProjector,MixedState,IdentityOp,SInvOperator, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, - XBasisState,YBasisState,ZBasisState,FockState, + XBasisState,YBasisState,ZBasisState,FockState,ContinuousCoherentState,DiscreteCoherentState, NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplacementOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock, diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index 33ebd37..d1c71a1 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -179,6 +179,7 @@ const CPHASE = CPHASEGate() abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice isexpr(::AbstractSingleBosonGate) = false +basis(x::AbstractSingleBosonOp) = x.basis basis(::AbstractSingleBosonGate) = inf_fock_basis @withmetadata struct NumberOp <: AbstractSingleBosonOp @@ -197,12 +198,12 @@ symbollabel(::DestroyOp) = "a" phase::Number basis::Basis end -symbollabel(x::PhaseShiftOp) = "Û($(x.phase))" +symbollabel(x::PhaseShiftOp) = "U($(x.phase))" @withmetadata struct DisplacementOp <: AbstractSingleBosonOp alpha::Number basis::Basis end -symbollabel(x::DisplacementOp) = "D̂($(x.alpha))" +symbollabel(x::DisplacementOp) = "D($(x.alpha))" """Number operator, also available as the constant `n̂`""" const N = const n̂ = NumberOp(inf_fock_basis) diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 744a9ff..bfda19c 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -95,17 +95,17 @@ RULES_ANTICOMMUTATOR = [ ] RULES_FOCK = [ - @rule(~o::_isa(DestroyOp) * ~k::(x->isequal(x, vac)) => SZeroKet()), + @rule(~o::_isa(DestroyOp) * ~k::_isequal(vac) => SZeroKet()), @rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => sqrt((~k).idx+1)*FockState((~k).idx+1, (~k).basis)), @rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => sqrt((~k).idx)*FockState((~k).idx-1, (~k).basis)), @rule(~o::_isa(NumberOp) * ~k::_isa(FockState) => (~k).idx*(~k)), - @rule(~o::_isa(DestroyOp) * ~k::_isa(ContinuousCoherentState) => (~k).idx*(~k)), + @rule(~o::_isa(DestroyOp) * ~k::_isa(ContinuousCoherentState) => (~k).alpha*(~k)), @rule(~o::_isa(PhaseShiftOp) * ~k::_isa(ContinuousCoherentState) => ContinuousCoherentState((~k).alpha * exp(-im*((~o).phase)), (~k).basis)), @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(~o1::_isa(PhaseShiftOp) * ~o2::_isa(DestroyOp) * dagger(~o1) => ~o2*exp(im*((~o1).phase))), @rule(dagger(~o1::_isa(DisplacementOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)), @rule(dagger(~o1::_isa(DisplacementOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), - @rule(~o::_isa(DisplacementOp) * ~k::(x->isequal(x, vac)) => ContinuousCoherentState((~o).alpha, (~k).basis)) + @rule(~o::_isa(DisplacementOp) * ~k::_isequal(vac) => ContinuousCoherentState((~o).alpha, (~k).basis)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/runtests.jl b/test/runtests.jl index ad0dce6..876521c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -37,6 +37,7 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA @doset "zero_obj" @doset "expand" @doset "pauli" +@doset "fock" VERSION >= v"1.9" && @doset "doctests" get(ENV,"JET_TEST","")=="true" && @doset "jet" diff --git a/test/test_fock.jl b/test/test_fock.jl index e8b9eb4..bde2dc2 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -4,7 +4,24 @@ using Test state1 = FockState(1, inf_fock_basis) state2 = FockState(2, inf_fock_basis) +cstate = ContinuousCoherentState(im, inf_fock_basis) +phase1 = PhaseShiftOp(0, inf_fock_basis) +phase2 = PhaseShiftOp(pi, inf_fock_basis) +displace = DisplacementOp(im, inf_fock_basis) -@testset "ladder operators" begin +@testset "ladder and number operators" begin + @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) @test isequal(qsimplify(Create*state1, rewriter=qsimplify_fock), sqrt(2)*state2) + @test isequal(qsimplify(Destroy*state2, rewriter=qsimplify_fock), sqrt(2)*state1) + @test isequal(qsimplify(N*state2, rewriter=qsimplify_fock), 2*state2) + @test isequal(qsimplify(Destroy*cstate, rewriter=qsimplify_fock), im*cstate) +end + +@testset "Displacement and phase operators" begin + @test isequal(qsimplify(phase1*cstate, rewriter=qsimplify_fock), ContinuousCoherentState(im, inf_fock_basis)) + @test isequal(qsimplify(dagger(phase2)*Destroy*phase2, rewriter=qsimplify_fock), Destroy*exp(-im*pi)) + @test isequal(qsimplify(phase2*Destroy*dagger(phase2), rewriter=qsimplify_fock), Destroy*exp(im*pi)) + @test isequal(qsimplify(dagger(displace)*Destroy*displace, rewriter=qsimplify_fock), Destroy + im*IdentityOp(inf_fock_basis)) + @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 \ No newline at end of file From 3f0e1f0c0320453ec09e1b3810f02577edf0e121 Mon Sep 17 00:00:00 2001 From: apkille Date: Fri, 12 Jul 2024 12:59:33 -0400 Subject: [PATCH 04/11] reorganize predef file and docstrings --- src/QSymbolicsBase/predefined.jl | 64 ------------- src/QSymbolicsBase/predefined_fock.jl | 133 ++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 64 deletions(-) create mode 100644 src/QSymbolicsBase/predefined_fock.jl diff --git a/src/QSymbolicsBase/predefined.jl b/src/QSymbolicsBase/predefined.jl index d1c71a1..789f045 100644 --- a/src/QSymbolicsBase/predefined.jl +++ b/src/QSymbolicsBase/predefined.jl @@ -25,24 +25,6 @@ symbollabel(x::YBasisState) = "Y$(num_to_sub(x.idx))" end symbollabel(x::ZBasisState) = "Z$(num_to_sub(x.idx))" -@withmetadata struct FockState <: SpecialKet - idx::Int - basis::Basis -end -symbollabel(x::FockState) = "$(x.idx)" - -@withmetadata struct DiscreteCoherentState <: SpecialKet - alpha::Number # TODO parameterize - basis::Basis -end -symbollabel(x::DiscreteCoherentState) = "$(x.alpha)" - -@withmetadata struct ContinuousCoherentState <: SpecialKet - alpha::Number # TODO parameterize - basis::Basis -end -symbollabel(x::ContinuousCoherentState) = "$(x.alpha)" - @withmetadata struct MomentumEigenState <: SpecialKet p::Number # TODO parameterize basis::Basis @@ -69,12 +51,6 @@ const Z1 = const Z₁ = const L0 = const L₀ = ZBasisState(1, qubit_basis) """Basis state of σᶻ""" const Z2 = const Z₂ = const L1 = const L₁ = ZBasisState(2, qubit_basis) -const inf_fock_basis = FockBasis(Inf,0.) -"""Vacuum basis state of n""" -const vac = const F₀ = const F0 = FockState(0,inf_fock_basis) -"""Single photon basis state of n""" -const F₁ = const F1 = FockState(1,inf_fock_basis) - ## # Gates and Operators on qubits ## @@ -172,46 +148,6 @@ const CNOT = CNOTGate() """CPHASE gate""" const CPHASE = CPHASEGate() -## -# Gates and Operators on harmonic oscillators -## - -abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end -abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice -isexpr(::AbstractSingleBosonGate) = false -basis(x::AbstractSingleBosonOp) = x.basis -basis(::AbstractSingleBosonGate) = inf_fock_basis - -@withmetadata struct NumberOp <: AbstractSingleBosonOp - basis::Basis -end -symbollabel(::NumberOp) = "n" -@withmetadata struct CreateOp <: AbstractSingleBosonOp - basis::Basis -end -symbollabel(::CreateOp) = "a†" -@withmetadata struct DestroyOp <: AbstractSingleBosonOp - basis::Basis -end -symbollabel(::DestroyOp) = "a" -@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp - phase::Number - basis::Basis -end -symbollabel(x::PhaseShiftOp) = "U($(x.phase))" -@withmetadata struct DisplacementOp <: AbstractSingleBosonOp - alpha::Number - basis::Basis -end -symbollabel(x::DisplacementOp) = "D($(x.alpha))" - -"""Number operator, also available as the constant `n̂`""" -const N = const n̂ = NumberOp(inf_fock_basis) -"""Creation operator, also available as the constant `âꜛ` - there is no unicode dagger superscript, so we use the uparrow""" -const Create = const âꜛ = CreateOp(inf_fock_basis) -"""Annihilation operator, also available as the constant `â`""" -const Destroy = const â = DestroyOp(inf_fock_basis) - ## # Other special or useful objects ## diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl new file mode 100644 index 0000000..d4e657c --- /dev/null +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -0,0 +1,133 @@ +## +# Predefined objects in the Fock space. +## + +"""Fock state in defined Fock basis.""" +@withmetadata struct FockState <: SpecialKet + idx::Int + basis::FockBasis +end +symbollabel(x::FockState) = "$(x.idx)" + +"""Coherent state in defined Fock basis.""" +@withmetadata struct CoherentState <: SpecialKet + alpha::Number # TODO parameterize + basis::FockBasis +end +symbollabel(x::CoherentState) = "$(x.alpha)" + +const inf_fock_basis = FockBasis(Inf,0.) +"""Vacuum basis state of n""" +const vac = const F₀ = const F0 = FockState(0,inf_fock_basis) +"""Single photon basis state of n""" +const F₁ = const F1 = FockState(1,inf_fock_basis) + +## +# Gates and Operators on harmonic oscillators +## + +abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end +abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice +isexpr(::AbstractSingleBosonGate) = false +basis(x::AbstractSingleBosonOp) = x.basis +basis(::AbstractSingleBosonGate) = inf_fock_basis + +"""Number operator in defined Fock basis. + +```jldoctest +julia> f = FockState(2, FockBasis(4)) +|2⟩ + +julia> num = NumberOp(FockBasis(4)) +n + +julia> qsimplify(num*f, rewriter=qsimplify_fock) +2|2⟩ +``` +""" +@withmetadata struct NumberOp <: AbstractSingleBosonOp + basis::FockBasis +end +symbollabel(::NumberOp) = "n" + +"""Creation (raising) operator in defined Fock basis. + +```jldoctest +julia> f = FockState(2, FockBasis(4)) +|2⟩ + +julia> create = CreateOp(FockBasis(4)) +a† + +julia> qsimplify(create*f, rewriter=qsimplify_fock) +1.7320508075688772|3⟩ +``` +""" +@withmetadata struct CreateOp <: AbstractSingleBosonOp + basis::FockBasis +end +symbollabel(::CreateOp) = "a†" + +"""Annihilation (lowering or destroy) operator in defined Fock basis. + +```jldoctest +julia> f = FockState(2, FockBasis(4)) +|2⟩ + +julia> destroy = DestroyOp(FockBasis(4)) +a + +julia> qsimplify(destroy*f, rewriter=qsimplify_fock) +1.4142135623730951|1⟩ +``` +""" +@withmetadata struct DestroyOp <: AbstractSingleBosonOp + basis::FockBasis +end +symbollabel(::DestroyOp) = "a" + +"""Phase-shift operator in defined Fock basis. + +```jldoctest +julia> c = CoherentState(im, FockBasis(4)) +|im⟩ + +julia> phase = PhaseShiftOp(pi, FockBasis(4)) +U(π) + +julia> qsimplify(phase*c, rewriter=qsimplify_fock) +|1.2246467991473532e-16 - 1.0im⟩ +``` +""" +@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp + phase::Number + basis::FockBasis +end +symbollabel(x::PhaseShiftOp) = "U($(x.phase))" + +"""Displacement operator in defined Fock basis. + +```jldoctest +julia> f = FockState(0, FockBasis(4)) +|0⟩ + +julia> displace = DisplaceOp(im, FockBasis(4)) +D(im) + +julia> qsimplify(displace*f, rewriter=qsimplify_fock) +|im⟩ +``` +""" +@withmetadata struct DisplaceOp <: AbstractSingleBosonOp + alpha::Number + basis::FockBasis +end +symbollabel(x::DisplaceOp) = "D($(x.alpha))" + +"""Number operator, also available as the constant `n̂`, in an infinite dimension Fock basis.""" +const N = const n̂ = NumberOp(inf_fock_basis) +"""Creation operator, also available as the constant `âꜛ`, in an infinite dimension Fock basis. +There is no unicode dagger superscript, so we use the uparrow""" +const Create = const âꜛ = CreateOp(inf_fock_basis) +"""Annihilation operator, also available as the constant `â`, in an infinite dimension Fock basis.""" +const Destroy = const â = DestroyOp(inf_fock_basis) \ No newline at end of file From 3256969b735b3ffa84e14897b7c4ce7d60f20e68 Mon Sep 17 00:00:00 2001 From: apkille Date: Fri, 12 Jul 2024 12:59:50 -0400 Subject: [PATCH 05/11] express and tests --- docs/src/fock.md | 18 +++---- ext/QuantumOpticsExt/QuantumOpticsExt.jl | 66 +++++++++++++++++++++--- src/QSymbolicsBase/QSymbolicsBase.jl | 5 +- src/QSymbolicsBase/rules.jl | 10 ++-- test/test_express_opt.jl | 9 ++++ test/test_fock.jl | 6 +-- 6 files changed, 88 insertions(+), 26 deletions(-) diff --git a/docs/src/fock.md b/docs/src/fock.md index 3bbd3b0..019e32b 100644 --- a/docs/src/fock.md +++ b/docs/src/fock.md @@ -23,13 +23,13 @@ Both vacuum (ground) and single-photon states in an infinite-dimension Fock basi - `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 state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with `ContinuousCoherentState(α::Number, basis::Basis)`: +To create quantum analogues of a classical harmonic oscillator, or monochromatic electromagnetic waves, we can define a coherent state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with `CoherentState(α::Number, basis::Basis)`: ```jldoctest julia> b = FockBasis(Inf, 0.0); -julia> c = ContinuousCoherentState(im, b) -|0 + im⟩ +julia> c = CoherentState(im, b) +|im⟩ ``` ## Operators @@ -63,7 +63,7 @@ julia> f = FockState(3, b); julia> num = N*f n|3⟩ -julia> qsimplify(num, rewirter=qsimplify_fock) +julia> qsimplify(num, rewriter=qsimplify_fock) 3|3⟩ ``` @@ -80,10 +80,10 @@ can be defined. Consider the following example: ```jldoctest julia> b = FockBasis(Inf, 0.0); -julia> displace = DisplacementOp(im, b) +julia> displace = DisplaceOp(im, b) D(im) -julia> c = qsimplify(displace*v, rewriter=qsimplify_fock) +julia> c = qsimplify(displace*vac, rewriter=qsimplify_fock) |im⟩ julia> phase = PhaseShiftOp(pi, b) @@ -92,7 +92,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 `DisplacementOp`. 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`. Then, we shifted its phase by $\pi$ with the phase shift operator (which is called with `PhaseShiftOp`) to get the result $|-i\rangle$. Summarized below are supported operators, which can be defined in any Fock basis. @@ -100,7 +100,7 @@ Summarized below are supported operators, which can be defined in any Fock basis - Creation operator: `CreateOp(basis::Basis)`, - Annihilation operator: `DestroyOp(basis::Basis)`, - Phase-shift operator: `PhaseShiftOp(phase::Number, basis:Basis)`, -- Displacement operator: `DisplacementOp(alpha::Number, basis::Basis)`. +- Displacement operator: `DisplaceOp(alpha::Number, basis::Basis)`. ## Numerical Conversions to QuantumOptics.jl @@ -121,7 +121,7 @@ Ket(dim=4) 0.0 + 0.0im 0.0 + 0.0im -julia> julia> express(CreateOp(b)) |> dense +julia> express(CreateOp(b)) |> dense Operator(dim=4x4) basis: Fock(cutoff=3) 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index 8dc1898..ba408ed 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -1,6 +1,7 @@ module QuantumOpticsExt using QuantumInterface, QuantumOpticsBase +using QuantumInterface: samebases using QuantumSymbolics using QuantumSymbolics: HGate, XGate, YGate, ZGate, CPHASEGate, CNOTGate, PauliP, PauliM, @@ -70,16 +71,67 @@ express_nolookup(s::XBasisState, ::QuantumOpticsRepr) = (_s₊,_s₋)[s.idx] express_nolookup(s::YBasisState, ::QuantumOpticsRepr) = (_i₊,_i₋)[s.idx] express_nolookup(s::ZBasisState, ::QuantumOpticsRepr) = (_l0,_l1)[s.idx] -express_nolookup(o::FockState, r::QuantumOpticsRepr) = fockstate(o.basis, o.idx) -express_nolookup(o::ContinuousCoherentState, r::QuantumOpticsRepr) = coherentstate(o.basis, o.alpha) -express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(o.basis) -express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(o.basis) -express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(o.basis) -express_nolookup(o::DisplacementOp, r::QuantumOpticsRepr) = displace(o.basis, o.alpha) +function express_nolookup(s::FockState, r::QuantumOpticsRepr) + b = basis(s) + i = s.idx + if !samebases(b, inf_fock_basis) + fockstate(b, i) + else + @warn "Fock space cutoff is not specified so we default to 2" + return fockstate(_bf2, i) + end +end +function express_nolookup(s::CoherentState, r::QuantumOpticsRepr) + b = basis(s) + alpha = s.alpha + if !samebases(b, inf_fock_basis) + coherentstate(b, alpha) + else + @warn "Fock space cutoff is not specified so we default to 2" + return coherentstate(_bf2, alpha) + end +end +function express_nolookup(o::NumberOp, r::QuantumOpticsRepr) + b = basis(o) + if !samebases(b, inf_fock_basis) + number(b) + else + @warn "Fock space cutoff is not specified so we default to 2" + return number(_bf2) + end +end +function express_nolookup(o::CreateOp, r::QuantumOpticsRepr) + b = basis(o) + if !samebases(b, inf_fock_basis) + create(b) + else + @warn "Fock space cutoff is not specified so we default to 2" + return create(_bf2) + end +end +function express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) + b = basis(o) + if !samebases(b, inf_fock_basis) + destroy(b) + else + @warn "Fock space cutoff is not specified so we default to 2" + return destroy(_bf2) + end +end +function express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) + b = basis(o) + alpha = o.alpha + if !samebases(b, inf_fock_basis) + displace(b, alpha) + else + @warn "Fock space cutoff is not specified so we default to 2" + return displace(_bf2, alpha) + end +end express_nolookup(x::MixedState, ::QuantumOpticsRepr) = identityoperator(basis(x))/length(basis(x)) # TODO there is probably a more efficient way to represent it function express_nolookup(x::IdentityOp, ::QuantumOpticsRepr) b = basis(x) - if b!=inf_fock_basis + if !samebases(b, inf_fock_basis) return identityoperator(basis(x)) # TODO there is probably a more efficient way to represent it else @warn "Fock space cutoff is not specified so we default to 2" diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index e8cbca8..d087019 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -38,8 +38,8 @@ export SymQObj,QObj, SProjector,MixedState,IdentityOp,SInvOperator, SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SDagger,SBraKet,SOuterKetBra, HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate, - XBasisState,YBasisState,ZBasisState,FockState,ContinuousCoherentState,DiscreteCoherentState, - NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplacementOp, + XBasisState,YBasisState,ZBasisState,FockState,CoherentState, + NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp, XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate, qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock, qexpand, @@ -164,6 +164,7 @@ include("basic_ops_inhomogeneous.jl") include("linalg.jl") include("predefined.jl") include("predefined_CPTP.jl") +include("predefined_fock.jl") ## # Symbolic and simplification rules diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index bfda19c..4e599db 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -99,13 +99,13 @@ RULES_FOCK = [ @rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => sqrt((~k).idx+1)*FockState((~k).idx+1, (~k).basis)), @rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => sqrt((~k).idx)*FockState((~k).idx-1, (~k).basis)), @rule(~o::_isa(NumberOp) * ~k::_isa(FockState) => (~k).idx*(~k)), - @rule(~o::_isa(DestroyOp) * ~k::_isa(ContinuousCoherentState) => (~k).alpha*(~k)), - @rule(~o::_isa(PhaseShiftOp) * ~k::_isa(ContinuousCoherentState) => ContinuousCoherentState((~k).alpha * exp(-im*((~o).phase)), (~k).basis)), + @rule(~o::_isa(DestroyOp) * ~k::_isa(CoherentState) => (~k).alpha*(~k)), + @rule(~o::_isa(PhaseShiftOp) * ~k::_isa(CoherentState) => CoherentState((~k).alpha * exp(-im*((~o).phase)), (~k).basis)), @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(DisplacementOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)), - @rule(dagger(~o1::_isa(DisplacementOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)), - @rule(~o::_isa(DisplacementOp) * ~k::_isequal(vac) => ContinuousCoherentState((~o).alpha, (~k).basis)) + @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, (~k).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 bb06d0a..6827c09 100644 --- a/test/test_express_opt.jl +++ b/test/test_express_opt.jl @@ -1,5 +1,6 @@ using Test using QuantumSymbolics +using QuantumSymbolics: inf_fock_basis import QuantumClifford import QuantumOptics @@ -36,3 +37,11 @@ op = N⊗X @test express(op*state) ≈ express(F1⊗Z2) 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) +@test express(N*F1) ≈ express(N)*express(F1) +@test express(Create*F1) ≈ express(Create)*express(F1) +@test express(Destroy*F1) ≈ express(Destroy)*express(F1) +@test express(displace*cstate) ≈ express(displace)*express(cstate) \ No newline at end of file diff --git a/test/test_fock.jl b/test/test_fock.jl index bde2dc2..f39b037 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -4,10 +4,10 @@ using Test state1 = FockState(1, inf_fock_basis) state2 = FockState(2, inf_fock_basis) -cstate = ContinuousCoherentState(im, inf_fock_basis) +cstate = CoherentState(im, inf_fock_basis) phase1 = PhaseShiftOp(0, inf_fock_basis) phase2 = PhaseShiftOp(pi, inf_fock_basis) -displace = DisplacementOp(im, inf_fock_basis) +displace = DisplaceOp(im, inf_fock_basis) @testset "ladder and number operators" begin @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) @@ -18,7 +18,7 @@ displace = DisplacementOp(im, inf_fock_basis) end @testset "Displacement and phase operators" begin - @test isequal(qsimplify(phase1*cstate, rewriter=qsimplify_fock), ContinuousCoherentState(im, inf_fock_basis)) + @test isequal(qsimplify(phase1*cstate, rewriter=qsimplify_fock), CoherentState(im, inf_fock_basis)) @test isequal(qsimplify(dagger(phase2)*Destroy*phase2, rewriter=qsimplify_fock), Destroy*exp(-im*pi)) @test isequal(qsimplify(phase2*Destroy*dagger(phase2), rewriter=qsimplify_fock), Destroy*exp(im*pi)) @test isequal(qsimplify(dagger(displace)*Destroy*displace, rewriter=qsimplify_fock), Destroy + im*IdentityOp(inf_fock_basis)) From a5b6c2b049deb94d8db2099f38ae925d0c78c6e0 Mon Sep 17 00:00:00 2001 From: apkille Date: Fri, 12 Jul 2024 13:16:33 -0400 Subject: [PATCH 06/11] update metadata --- src/QSymbolicsBase/QSymbolicsBase.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index d087019..1237d7c 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -141,7 +141,6 @@ function Base.isequal(x::X,y::Y) where {X<:SymQObj, Y<:SymQObj} end Base.isequal(::SymQObj, ::Symbolic{Complex}) = false Base.isequal(::Symbolic{Complex}, ::SymQObj) = false -Base.isequal(x::Metadata, y::Metadata) = isequal(x.express_cache,y.express_cache) # TODO check that this does not cause incredibly bad runtime performance # use a macro to provide specializations if that is indeed the case From d6fcf94739aa03bd3c9774ff0f463c430b574824 Mon Sep 17 00:00:00 2001 From: apkille Date: Wed, 17 Jul 2024 15:51:16 -0400 Subject: [PATCH 07/11] add fock.md to make.jl --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/make.jl b/docs/make.jl index 4092076..960cd77 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -26,6 +26,7 @@ function main() pages = [ "QuantumSymbolics.jl" => "index.md", "Qubit Basis Choice" => "qubit_basis.md", + "Fock States" => "fock.md", "Express Functionality" => "express.md", "API" => "API.md", ] From 9ca5695e68aa9101a52953f457bcabab5faeeb90 Mon Sep 17 00:00:00 2001 From: apkille Date: Tue, 23 Jul 2024 17:48:16 -0400 Subject: [PATCH 08/11] review changes --- docs/make.jl | 2 +- docs/src/QHO.md | 159 +++++++++++++++++++++++ docs/src/express.md | 25 +++- docs/src/fock.md | 147 --------------------- ext/QuantumOpticsExt/QuantumOpticsExt.jl | 73 +---------- src/QSymbolicsBase/QSymbolicsBase.jl | 4 +- src/QSymbolicsBase/express.jl | 12 +- src/QSymbolicsBase/predefined_fock.jl | 50 +++---- src/QSymbolicsBase/rules.jl | 8 +- test/test_fock.jl | 19 +-- 10 files changed, 244 insertions(+), 255 deletions(-) create mode 100644 docs/src/QHO.md delete mode 100644 docs/src/fock.md diff --git a/docs/make.jl b/docs/make.jl index 0fbb0ba..6bce4c3 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -27,7 +27,7 @@ function main() "Getting Started with QuantumSymbolics.jl" => "introduction.md", "Express Functionality" => "express.md", "Qubit Basis Choice" => "qubit_basis.md", - "Fock States" => "fock.md", + "Quantum Harmonic Oscillators" => "QHO.md", "API" => "API.md", ] ) diff --git a/docs/src/QHO.md b/docs/src/QHO.md new file mode 100644 index 0000000..df084c2 --- /dev/null +++ b/docs/src/QHO.md @@ -0,0 +1,159 @@ +# Quantum Harmonic Oscillators + +```@meta +DocTestSetup = quote + using QuantumSymbolics, QuantumOptics +end +``` + +In this section, we describe symbolic representations of bosonic systems in QuantumSymbolics, which can be numerically translated to [`QuantumOptics.jl`](https://github.com/qojulia/QuantumOptics.jl). + +## States + +A Fock state is the number of excitation quanta of a single quantum harmonic oscillator. In the following example, we create a `FockState` with 3 quanta in an infinite-dimension Fock space: + +```jldoctest +julia> f = FockState(3) +|3⟩ +``` + +Both vacuum (ground) and single-photon states are defined as constants in both unicode and ASCII for convenience: + +- `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 state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with `CoherentState(α::Number)`: + +```jldoctest +julia> c = CoherentState(im) +|im⟩ +``` +!!! tip "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 considered Fock states. However, this is simply a naming convention, and symbolic and numerical results are not affected by it. + +## 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: + +```jldoctest +julia> f = FockState(3); + +julia> raise = Create*f +a†|3⟩ + +julia> qsimplify(raise, rewriter=qsimplify_fock) +(sqrt(4))|4⟩ + +julia> lower = Destroy*f +a|3⟩ + +julia> qsimplify(lower, rewriter=qsimplify_fock) +(sqrt(3))|2⟩ +``` +Or, we can apply the number operator $\hat{n}$ to our Fock state: + +```jldoctest +julia> f = FockState(3); + +julia> num = N*f +n|3⟩ + +julia> qsimplify(num, rewriter=qsimplify_fock) +3|3⟩ +``` + +Constants are defined for number and ladder operators in unicode and ASCII: + +- `N = n̂` $=\hat{n}$, +- `Create = âꜛ` $=\hat{a}^{\dagger}$, +- `Destroy = â` $=\hat{a}$. + +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 +julia> displace = DisplaceOp(im) +D(im) + +julia> c = qsimplify(displace*vac, rewriter=qsimplify_fock) +|im⟩ + +julia> phase = PhaseShiftOp(pi) +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$. + +Summarized below are supported bosonic operators. + +- Number operator: `NumberOp()`, +- Creation operator: `CreateOp()`, +- Annihilation operator: `DestroyOp()`, +- Phase-shift operator: `PhaseShiftOp(phase::Number)`, +- Displacement operator: `DisplaceOp(alpha::Number)`. + +## Numerical Conversions to QuantumOptics.jl + +Bosonic systems can be translated to the ket representation with `express`. For instance: + +```jldoctest +julia> f = FockState(1); + +julia> express(f) +Ket(dim=3) + basis: Fock(cutoff=2) + 0.0 + 0.0im + 1.0 + 0.0im + 0.0 + 0.0im + +julia> express(Create) |> dense +Operator(dim=3x3) + basis: Fock(cutoff=2) + 0.0+0.0im 0.0+0.0im 0.0+0.0im + 1.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 1.41421+0.0im 0.0+0.0im + +julia> express(Create*f) +Ket(dim=3) + basis: Fock(cutoff=2) + 0.0 + 0.0im + 0.0 + 0.0im + 1.4142135623730951 + 0.0im + +julia> express(Destroy*f) +Ket(dim=3) + basis: Fock(cutoff=2) + 1.0 + 0.0im + 0.0 + 0.0im + 0.0 + 0.0im +``` + +!!! 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 pass the argument `QuantumOpticsRepr(::Int)` to `express`. + +If we wish to specify a different numerical cutoff, say 4, to the previous examples, then we rewrite them as follows: + +```jldoctest +julia> f = FockState(1); + +julia> express(f, QuantumOpticsRepr(4)) +Ket(dim=5) + basis: Fock(cutoff=4) + 0.0 + 0.0im + 1.0 + 0.0im + 0.0 + 0.0im + 0.0 + 0.0im + 0.0 + 0.0im + +julia> express(Create, QuantumOpticsRepr(4)) |> dense +Operator(dim=5x5) + basis: Fock(cutoff=4) + 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 1.41421+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 1.73205+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.0+0.0im 2.0+0.0im 0.0+0.0im +``` \ No newline at end of file diff --git a/docs/src/express.md b/docs/src/express.md index 8fbb1e8..8565967 100644 --- a/docs/src/express.md +++ b/docs/src/express.md @@ -6,7 +6,7 @@ DocTestSetup = quote end ``` -A principle feature of `QuantumSymbolics` is to numerically represent symbolic quantum expressions in various formalisms using [`express`](@ref). In particular, one can translate symbolic logic to back-end toolboxes such as `QuantumOptics.jl` or `QuantumClifford.jl` for simulating quantum systems with great flexibiity. +A principle feature of `QuantumSymbolics` is to numerically represent symbolic quantum expressions in various formalisms using [`express`](@ref). In particular, one can translate symbolic logic to back-end toolboxes such as [`QuantumOptics.jl`](https://github.com/qojulia/QuantumOptics.jl) or [`QuantumClifford.jl`](https://github.com/QuantumSavory/QuantumClifford.jl) for simulating quantum systems with great flexibiity. As a straightforward example, consider the spin-up state $|\uparrow\rangle = |0\rangle$, the eigenstate of the Pauli operator $Z$, which can be expressed in `QuantumSymbolics` as follows: @@ -14,7 +14,7 @@ As a straightforward example, consider the spin-up state $|\uparrow\rangle = |0\ using QuantumSymbolics, QuantumClifford, QuantumOptics # hide ψ = Z1 ``` -Using [`express`](@ref), we can translate this symbolic object into its numerical state vector form in `QuantumOptics.jl`. +Using [`express`](@ref), we can translate this symbolic object into its numerical state vector form in [`QuantumOptics.jl`](https://github.com/qojulia/QuantumOptics.jl). ```@example 1 express(ψ) @@ -26,7 +26,7 @@ By default, [`express`](@ref) converts a quantum object with `QuantumOpticRepr`. ψ.metadata ``` -The caching feature of [`express`](@ref) prevents a specific representation for a symbolic quantum object from being computed more than once. This becomes handy for translations of more complex operations, which can become computationally expensive. We also have the ability to express $|Z_1\rangle$ in the Clifford formalism with `QuantumClifford.jl`: +The caching feature of [`express`](@ref) prevents a specific representation for a symbolic quantum object from being computed more than once. This becomes handy for translations of more complex operations, which can become computationally expensive. We also have the ability to express $|Z_1\rangle$ in the Clifford formalism with [`QuantumClifford.jl`](https://github.com/QuantumSavory/QuantumClifford.jl): ```@example 1 express(ψ, CliffordRepr()) @@ -56,4 +56,23 @@ julia> express(σʸ, CliffordRepr(), UseAsObservable()) julia> express(σʸ, CliffordRepr(), UseAsOperation()) sY +``` +Another edge case is translations with `QuantumOpticsRepr`, where we can additionally define a finite cutoff for bosonic states and operators, as discussed in the [quantum harmonic oscillators page](@ref Quantum-Harmonic-Oscillators). The default cutoff for such objects is 2, however a different cutoff can be specified by passing an integer to `QuantumOpticsRepr` in an `express` call. Let us see an example with the number operator: + +```jldoctest +julia> express(N) |> dense +Operator(dim=3x3) + basis: Fock(cutoff=2) + 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 1.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 2.0+0.0im + +julia> express(N, QuantumOpticsRepr(4)) |> dense +Operator(dim=5x5) + basis: Fock(cutoff=4) + 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 2.0+0.0im 0.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.0+0.0im 3.0+0.0im 0.0+0.0im + 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 4.0+0.0im ``` \ No newline at end of file diff --git a/docs/src/fock.md b/docs/src/fock.md deleted file mode 100644 index 019e32b..0000000 --- a/docs/src/fock.md +++ /dev/null @@ -1,147 +0,0 @@ -# Fock States - -```@meta -DocTestSetup = quote - using QuantumSymbolics, QuantumOptics -end -``` - -In this section, we describe symbolic representations of Fock states in QuantumSymbolics, which can be numerically translated to `QuantumOptics.jl`. A Fock state is the number state representation $|n\rangle$ of a system with $n$ particles. - -One can define a basis of the Fock space with `FockBasis(N, offset=0)`, where `N` is the highest available Fock state and `offset` is the lowest cutoff state, which is by default zero. In the following example, we create a `FockState` with 3 quanta in an infinite-dimension Fock space: - -```jldoctest -julia> b = FockBasis(Inf, 0.0) -Fock(cutoff=Inf) - -julia> f = FockState(3, b) -|3⟩ -``` - -Both vacuum (ground) and single-photon states in an infinite-dimension Fock basis are defined as constants in both unicode and ASCII for convenience: - -- `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 state $|\alpha\rangle$, where $\alpha$ is a complex amplitude, with `CoherentState(α::Number, basis::Basis)`: - -```jldoctest -julia> b = FockBasis(Inf, 0.0); - -julia> c = CoherentState(im, b) -|im⟩ -``` - -## Operators - -Operations on fock states are supported, and can be simplified with `qsimplify` and its rewriter `qsimplify_fock`. For instance, we can apply the raising (creation) $a^{\dagger}$ and lowering (annihilation or destroy) $a$ operators on a fock state as follows: - -```jldoctest -julia> b = FockBasis(Inf, 0.0); - -julia> f = FockState(3, b); - -julia> raise = Create*f -a†|3⟩ - -julia> qsimplify(raise, rewriter=qsimplify_fock) -2.0|4⟩ - -julia> lower = Destroy*f -a|3⟩ - -julia> qsimplify(lower, rewriter=qsimplify_fock) -1.7320508075688772|2⟩ -``` -Or, we can apply the number operator $\hat{n}$ to our fock state: - -```jldoctest -julia> b = FockBasis(Inf, 0.0); - -julia> f = FockState(3, b); - -julia> num = N*f -n|3⟩ - -julia> qsimplify(num, rewriter=qsimplify_fock) -3|3⟩ -``` - -In the infinite dimension case for Fock states, constants are defined for number and ladder operators: - -- `N = n̂` $=\hat{n}$, -- `Create = âꜛ` $=\hat{a}^{\dagger}$, -- `Destroy = â` $=\hat{a}$. - -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. Consider the following example: - -```jldoctest -julia> b = FockBasis(Inf, 0.0); - -julia> displace = DisplaceOp(im, b) -D(im) - -julia> c = qsimplify(displace*vac, rewriter=qsimplify_fock) -|im⟩ - -julia> phase = PhaseShiftOp(pi, b) -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$. - -Summarized below are supported operators, which can be defined in any Fock basis. - -- Number operator: `NumberOp(basis:Basis)`, -- Creation operator: `CreateOp(basis::Basis)`, -- Annihilation operator: `DestroyOp(basis::Basis)`, -- Phase-shift operator: `PhaseShiftOp(phase::Number, basis:Basis)`, -- Displacement operator: `DisplaceOp(alpha::Number, basis::Basis)`. - -## Numerical Conversions to QuantumOptics.jl - -Fock systems can be translated to the ket representation with `express`. For instance: - -```jldoctest -julia> using QuantumOptics - -julia> b = FockBasis(3); - -julia> f = FockState(1, b); - -julia> express(f) -Ket(dim=4) - basis: Fock(cutoff=3) - 0.0 + 0.0im - 1.0 + 0.0im - 0.0 + 0.0im - 0.0 + 0.0im - -julia> express(CreateOp(b)) |> dense -Operator(dim=4x4) - basis: Fock(cutoff=3) - 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 1.41421+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 1.73205+0.0im 0.0+0.0im - -julia> express(CreateOp(b)*f) -Ket(dim=4) - basis: Fock(cutoff=3) - 0.0 + 0.0im - 0.0 + 0.0im - 1.4142135623730951 + 0.0im - 0.0 + 0.0im - -julia> express(DestroyOp(b)*f) -Ket(dim=4) - basis: Fock(cutoff=3) - 1.0 + 0.0im - 0.0 + 0.0im - 0.0 + 0.0im - 0.0 + 0.0im -``` \ No newline at end of file diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index ba408ed..1cb1b30 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -71,73 +71,14 @@ express_nolookup(s::XBasisState, ::QuantumOpticsRepr) = (_s₊,_s₋)[s.idx] express_nolookup(s::YBasisState, ::QuantumOpticsRepr) = (_i₊,_i₋)[s.idx] express_nolookup(s::ZBasisState, ::QuantumOpticsRepr) = (_l0,_l1)[s.idx] -function express_nolookup(s::FockState, r::QuantumOpticsRepr) - b = basis(s) - i = s.idx - if !samebases(b, inf_fock_basis) - fockstate(b, i) - else - @warn "Fock space cutoff is not specified so we default to 2" - return fockstate(_bf2, i) - end -end -function express_nolookup(s::CoherentState, r::QuantumOpticsRepr) - b = basis(s) - alpha = s.alpha - if !samebases(b, inf_fock_basis) - coherentstate(b, alpha) - else - @warn "Fock space cutoff is not specified so we default to 2" - return coherentstate(_bf2, alpha) - end -end -function express_nolookup(o::NumberOp, r::QuantumOpticsRepr) - b = basis(o) - if !samebases(b, inf_fock_basis) - number(b) - else - @warn "Fock space cutoff is not specified so we default to 2" - return number(_bf2) - end -end -function express_nolookup(o::CreateOp, r::QuantumOpticsRepr) - b = basis(o) - if !samebases(b, inf_fock_basis) - create(b) - else - @warn "Fock space cutoff is not specified so we default to 2" - return create(_bf2) - end -end -function express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) - b = basis(o) - if !samebases(b, inf_fock_basis) - destroy(b) - else - @warn "Fock space cutoff is not specified so we default to 2" - return destroy(_bf2) - end -end -function express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) - b = basis(o) - alpha = o.alpha - if !samebases(b, inf_fock_basis) - displace(b, alpha) - else - @warn "Fock space cutoff is not specified so we default to 2" - return displace(_bf2, alpha) - end -end +express_nolookup(s::FockState, r::QuantumOpticsRepr) = fockstate(FockBasis(r.fock_cutoff),s.idx) +express_nolookup(s::CoherentState, r::QuantumOpticsRepr) = coherentstate(FockBasis(r.fock_cutoff),s.alpha) +express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(FockBasis(r.fock_cutoff)) +express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(FockBasis(r.fock_cutoff)) +express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(FockBasis(r.fock_cutoff)) +express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) = displace(FockBasis(r.fock_cutoff), o.alpha) express_nolookup(x::MixedState, ::QuantumOpticsRepr) = identityoperator(basis(x))/length(basis(x)) # TODO there is probably a more efficient way to represent it -function express_nolookup(x::IdentityOp, ::QuantumOpticsRepr) - b = basis(x) - if !samebases(b, inf_fock_basis) - return identityoperator(basis(x)) # TODO there is probably a more efficient way to represent it - else - @warn "Fock space cutoff is not specified so we default to 2" - return identityoperator(_bf2) - end -end +express_nolookup(x::IdentityOp, r::QuantumOpticsRepr) = identityoperator(FockBasis(r.fock_cutoff)) express_nolookup(p::PauliNoiseCPTP, ::QuantumOpticsRepr) = LazySuperSum(SpinBasis(1//2), [1-p.px-p.py-p.pz,p.px,p.py,p.pz], [LazyPrePost(_id,_id),LazyPrePost(_x,_x),LazyPrePost(_y,_y),LazyPrePost(_z,_z)]) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index df99f5d..1f99af9 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -1,5 +1,5 @@ using Symbolics -import Symbolics: simplify +import Symbolics: simplify, Term using SymbolicUtils import SymbolicUtils: Symbolic,_isone,flatten_term,isnotflat,Chain,Fixpoint,Prewalk,sorted_arguments using TermInterface @@ -27,7 +27,7 @@ export SymQObj,QObj, I,X,Y,Z,σˣ,σʸ,σᶻ,Pm,Pp,σ₋,σ₊, H,CNOT,CPHASE,XCX,XCY,XCZ,YCX,YCY,YCZ,ZCX,ZCY,ZCZ, X1,X2,Y1,Y2,Z1,Z2,X₁,X₂,Y₁,Y₂,Z₁,Z₂,L0,L1,Lp,Lm,Lpi,Lmi,L₀,L₁,L₊,L₋,L₊ᵢ,L₋ᵢ, - vac,F₀,F0,F₁,F1, + vac,F₀,F0,F₁,F1,inf_fock_basis, N,n̂,Create,âꜛ,Destroy,â,basis,SpinBasis,FockBasis, SBra,SKet,SOperator,SHermitianOperator,SUnitaryOperator,SHermitianUnitaryOperator,SSuperOperator, @ket,@bra,@op,@superop, diff --git a/src/QSymbolicsBase/express.jl b/src/QSymbolicsBase/express.jl index 8fd6466..8e35595 100644 --- a/src/QSymbolicsBase/express.jl +++ b/src/QSymbolicsBase/express.jl @@ -78,7 +78,17 @@ end ## """Representation using kets, bras, density matrices, and superoperators governed by `QuantumOptics.jl`.""" -struct QuantumOpticsRepr <: AbstractRepresentation end +struct QuantumOpticsRepr <: AbstractRepresentation + fock_cutoff::Union{Nothing, Int} + function QuantumOpticsRepr(fock_cutoff::Union{Nothing, Int}) + if isnothing(fock_cutoff) + return new(2) + else + return new(fock_cutoff) + end + end +end +QuantumOpticsRepr() = QuantumOpticsRepr(nothing) """Similar to `QuantumOpticsRepr`, but using trajectories instead of superoperators.""" struct QuantumMCRepr <: AbstractRepresentation end """Representation using tableaux governed by `QuantumClifford.jl`""" diff --git a/src/QSymbolicsBase/predefined_fock.jl b/src/QSymbolicsBase/predefined_fock.jl index d4e657c..5434194 100644 --- a/src/QSymbolicsBase/predefined_fock.jl +++ b/src/QSymbolicsBase/predefined_fock.jl @@ -7,6 +7,7 @@ idx::Int basis::FockBasis end +FockState(idx::Int) = FockState(idx, inf_fock_basis) symbollabel(x::FockState) = "$(x.idx)" """Coherent state in defined Fock basis.""" @@ -14,13 +15,14 @@ symbollabel(x::FockState) = "$(x.idx)" alpha::Number # TODO parameterize basis::FockBasis end +CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis) symbollabel(x::CoherentState) = "$(x.alpha)" const inf_fock_basis = FockBasis(Inf,0.) """Vacuum basis state of n""" -const vac = const F₀ = const F0 = FockState(0,inf_fock_basis) +const vac = const F₀ = const F0 = FockState(0) """Single photon basis state of n""" -const F₁ = const F1 = FockState(1,inf_fock_basis) +const F₁ = const F1 = FockState(1) ## # Gates and Operators on harmonic oscillators @@ -29,70 +31,72 @@ const F₁ = const F1 = FockState(1,inf_fock_basis) abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice isexpr(::AbstractSingleBosonGate) = false -basis(x::AbstractSingleBosonOp) = x.basis -basis(::AbstractSingleBosonGate) = inf_fock_basis +basis(x::AbstractSingleBosonOp) = inf_fock_basis -"""Number operator in defined Fock basis. +"""Number operator. ```jldoctest -julia> f = FockState(2, FockBasis(4)) +julia> f = FockState(2) |2⟩ -julia> num = NumberOp(FockBasis(4)) +julia> num = NumberOp() n julia> qsimplify(num*f, rewriter=qsimplify_fock) 2|2⟩ ``` """ -@withmetadata struct NumberOp <: AbstractSingleBosonOp +@withmetadata struct NumberOp <: AbstractSingleBosonOp basis::FockBasis end +NumberOp() = NumberOp(inf_fock_basis) symbollabel(::NumberOp) = "n" -"""Creation (raising) operator in defined Fock basis. +"""Creation (raising) operator. ```jldoctest -julia> f = FockState(2, FockBasis(4)) +julia> f = FockState(2) |2⟩ -julia> create = CreateOp(FockBasis(4)) +julia> create = CreateOp() a† julia> qsimplify(create*f, rewriter=qsimplify_fock) -1.7320508075688772|3⟩ +(sqrt(3))|3⟩ ``` """ @withmetadata struct CreateOp <: AbstractSingleBosonOp basis::FockBasis end +CreateOp() = CreateOp(inf_fock_basis) symbollabel(::CreateOp) = "a†" """Annihilation (lowering or destroy) operator in defined Fock basis. ```jldoctest -julia> f = FockState(2, FockBasis(4)) +julia> f = FockState(2) |2⟩ -julia> destroy = DestroyOp(FockBasis(4)) +julia> destroy = DestroyOp() a julia> qsimplify(destroy*f, rewriter=qsimplify_fock) -1.4142135623730951|1⟩ +(sqrt(2))|1⟩ ``` """ @withmetadata struct DestroyOp <: AbstractSingleBosonOp basis::FockBasis end +DestroyOp() = DestroyOp(inf_fock_basis) symbollabel(::DestroyOp) = "a" """Phase-shift operator in defined Fock basis. ```jldoctest -julia> c = CoherentState(im, FockBasis(4)) +julia> c = CoherentState(im) |im⟩ -julia> phase = PhaseShiftOp(pi, FockBasis(4)) +julia> phase = PhaseShiftOp(pi) U(π) julia> qsimplify(phase*c, rewriter=qsimplify_fock) @@ -103,15 +107,16 @@ julia> qsimplify(phase*c, rewriter=qsimplify_fock) 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. ```jldoctest -julia> f = FockState(0, FockBasis(4)) +julia> f = FockState(0) |0⟩ -julia> displace = DisplaceOp(im, FockBasis(4)) +julia> displace = DisplaceOp(im) D(im) julia> qsimplify(displace*f, rewriter=qsimplify_fock) @@ -122,12 +127,13 @@ julia> qsimplify(displace*f, rewriter=qsimplify_fock) 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.""" -const N = const n̂ = NumberOp(inf_fock_basis) +const N = const n̂ = NumberOp() """Creation operator, also available as the constant `âꜛ`, in an infinite dimension Fock basis. There is no unicode dagger superscript, so we use the uparrow""" -const Create = const âꜛ = CreateOp(inf_fock_basis) +const Create = const âꜛ = CreateOp() """Annihilation operator, also available as the constant `â`, in an infinite dimension Fock basis.""" -const Destroy = const â = DestroyOp(inf_fock_basis) \ No newline at end of file +const Destroy = const â = DestroyOp() \ No newline at end of file diff --git a/src/QSymbolicsBase/rules.jl b/src/QSymbolicsBase/rules.jl index 6a645a7..ac31f95 100644 --- a/src/QSymbolicsBase/rules.jl +++ b/src/QSymbolicsBase/rules.jl @@ -96,16 +96,16 @@ RULES_ANTICOMMUTATOR = [ RULES_FOCK = [ @rule(~o::_isa(DestroyOp) * ~k::_isequal(vac) => SZeroKet()), - @rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => sqrt((~k).idx+1)*FockState((~k).idx+1, (~k).basis)), - @rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => sqrt((~k).idx)*FockState((~k).idx-1, (~k).basis)), + @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)), @rule(~o::_isa(DestroyOp) * ~k::_isa(CoherentState) => (~k).alpha*(~k)), - @rule(~o::_isa(PhaseShiftOp) * ~k::_isa(CoherentState) => CoherentState((~k).alpha * exp(-im*((~o).phase)), (~k).basis)), + @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(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha, (~k).basis)) + @rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)) ] RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK] diff --git a/test/test_fock.jl b/test/test_fock.jl index f39b037..ef6c0da 100644 --- a/test/test_fock.jl +++ b/test/test_fock.jl @@ -1,24 +1,25 @@ using QuantumSymbolics using QuantumSymbolics: inf_fock_basis +using Symbolics: Term using Test -state1 = FockState(1, inf_fock_basis) -state2 = FockState(2, inf_fock_basis) -cstate = CoherentState(im, inf_fock_basis) -phase1 = PhaseShiftOp(0, inf_fock_basis) -phase2 = PhaseShiftOp(pi, inf_fock_basis) -displace = DisplaceOp(im, inf_fock_basis) +state1 = FockState(1) +state2 = FockState(2) +cstate = CoherentState(im) +phase1 = PhaseShiftOp(0) +phase2 = PhaseShiftOp(pi) +displace = DisplaceOp(im) @testset "ladder and number operators" begin @test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet()) - @test isequal(qsimplify(Create*state1, rewriter=qsimplify_fock), sqrt(2)*state2) - @test isequal(qsimplify(Destroy*state2, rewriter=qsimplify_fock), sqrt(2)*state1) + @test isequal(qsimplify(Create*state1, rewriter=qsimplify_fock), Term(sqrt, [2])*state2) + @test isequal(qsimplify(Destroy*state2, rewriter=qsimplify_fock), Term(sqrt, [2])*state1) @test isequal(qsimplify(N*state2, rewriter=qsimplify_fock), 2*state2) @test isequal(qsimplify(Destroy*cstate, rewriter=qsimplify_fock), im*cstate) end @testset "Displacement and phase operators" begin - @test isequal(qsimplify(phase1*cstate, rewriter=qsimplify_fock), CoherentState(im, inf_fock_basis)) + @test isequal(qsimplify(phase1*cstate, rewriter=qsimplify_fock), CoherentState(im)) @test isequal(qsimplify(dagger(phase2)*Destroy*phase2, rewriter=qsimplify_fock), Destroy*exp(-im*pi)) @test isequal(qsimplify(phase2*Destroy*dagger(phase2), rewriter=qsimplify_fock), Destroy*exp(im*pi)) @test isequal(qsimplify(dagger(displace)*Destroy*displace, rewriter=qsimplify_fock), Destroy + im*IdentityOp(inf_fock_basis)) From 04bc2c51c58ba308079540ed80f23ed9a21ed097 Mon Sep 17 00:00:00 2001 From: apkille Date: Tue, 23 Jul 2024 17:52:30 -0400 Subject: [PATCH 09/11] rm space --- src/QSymbolicsBase/QSymbolicsBase.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QSymbolicsBase/QSymbolicsBase.jl b/src/QSymbolicsBase/QSymbolicsBase.jl index 1f99af9..512f316 100644 --- a/src/QSymbolicsBase/QSymbolicsBase.jl +++ b/src/QSymbolicsBase/QSymbolicsBase.jl @@ -1,5 +1,5 @@ using Symbolics -import Symbolics: simplify, Term +import Symbolics: simplify,Term using SymbolicUtils import SymbolicUtils: Symbolic,_isone,flatten_term,isnotflat,Chain,Fixpoint,Prewalk,sorted_arguments using TermInterface From 7e9eb659ff2ffa2a29cda02019f5865dace0b5ae Mon Sep 17 00:00:00 2001 From: Stefan Krastanov Date: Sat, 3 Aug 2024 20:41:55 -0400 Subject: [PATCH 10/11] some minor stylistic changes and and also breaking your code with a non-working example --- docs/src/QHO.md | 12 ++++++------ docs/src/express.md | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/src/QHO.md b/docs/src/QHO.md index df084c2..764e9eb 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 the number of excitation quanta of a single quantum harmonic oscillator. 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` with 3 quanta in an infinite-dimension Fock space: ```jldoctest julia> f = FockState(3) @@ -22,14 +22,14 @@ 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 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(α::Number)`: ```jldoctest julia> c = CoherentState(im) |im⟩ ``` -!!! tip "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 considered Fock states. However, this is simply a naming convention, and symbolic and numerical results are not affected by it. +!!! 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. ## Operators @@ -132,14 +132,14 @@ 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 pass the argument `QuantumOpticsRepr(::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`. If we wish to specify a different numerical cutoff, say 4, to the previous examples, then we rewrite them as follows: ```jldoctest julia> f = FockState(1); -julia> express(f, QuantumOpticsRepr(4)) +julia> express(f, QuantumOpticsRepr(cutoff=4)) Ket(dim=5) basis: Fock(cutoff=4) 0.0 + 0.0im diff --git a/docs/src/express.md b/docs/src/express.md index 8565967..a8af033 100644 --- a/docs/src/express.md +++ b/docs/src/express.md @@ -14,6 +14,7 @@ As a straightforward example, consider the spin-up state $|\uparrow\rangle = |0\ using QuantumSymbolics, QuantumClifford, QuantumOptics # hide ψ = Z1 ``` + Using [`express`](@ref), we can translate this symbolic object into its numerical state vector form in [`QuantumOptics.jl`](https://github.com/qojulia/QuantumOptics.jl). ```@example 1 @@ -57,6 +58,7 @@ julia> express(σʸ, CliffordRepr(), UseAsObservable()) julia> express(σʸ, CliffordRepr(), UseAsOperation()) sY ``` + Another edge case is translations with `QuantumOpticsRepr`, where we can additionally define a finite cutoff for bosonic states and operators, as discussed in the [quantum harmonic oscillators page](@ref Quantum-Harmonic-Oscillators). The default cutoff for such objects is 2, however a different cutoff can be specified by passing an integer to `QuantumOpticsRepr` in an `express` call. Let us see an example with the number operator: ```jldoctest @@ -67,7 +69,7 @@ Operator(dim=3x3) 0.0+0.0im 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 2.0+0.0im -julia> express(N, QuantumOpticsRepr(4)) |> dense +julia> express(N, QuantumOpticsRepr(cutoff=4)) |> dense Operator(dim=5x5) basis: Fock(cutoff=4) 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im From 612050b07cbe3e988007c9770dcee04054be5c62 Mon Sep 17 00:00:00 2001 From: apkille Date: Tue, 6 Aug 2024 17:20:08 -0400 Subject: [PATCH 11/11] update changelog and kwdef QuantumOpticsRepr --- CHANGELOG.md | 6 +++++- Project.toml | 2 +- ext/QuantumOpticsExt/QuantumOpticsExt.jl | 16 ++++++++-------- src/QSymbolicsBase/express.jl | 12 ++---------- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d71bd9..6f5c779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,12 @@ # News -## v0.3.5 - dev +## v0.4.0 - 2024-08-03 - Cleaned up metadata decoration of struct definitions. +- Added documentation for quantum harmonic oscillators. +- Added phase-shift and displacement operators `DisplaceOp` and `PhaseShiftOp`. +- Simplification rules for Fock objects. +- **(breaking)** `FockBasisState` was renamed to `FockState`. ## v0.3.4 - 2024-07-22 diff --git a/Project.toml b/Project.toml index 1ed77ea..ad0c256 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.3.5-dev" +version = "0.4.0" [deps] Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" diff --git a/ext/QuantumOpticsExt/QuantumOpticsExt.jl b/ext/QuantumOpticsExt/QuantumOpticsExt.jl index 1cb1b30..311e171 100644 --- a/ext/QuantumOpticsExt/QuantumOpticsExt.jl +++ b/ext/QuantumOpticsExt/QuantumOpticsExt.jl @@ -10,7 +10,7 @@ using QuantumSymbolics: NumberOp, CreateOp, DestroyOp, FockState, MixedState, IdentityOp, - qubit_basis, inf_fock_basis + qubit_basis import QuantumSymbolics: express, express_nolookup using TermInterface using TermInterface: isexpr, head, operation, arguments, metadata @@ -71,14 +71,14 @@ express_nolookup(s::XBasisState, ::QuantumOpticsRepr) = (_s₊,_s₋)[s.idx] express_nolookup(s::YBasisState, ::QuantumOpticsRepr) = (_i₊,_i₋)[s.idx] express_nolookup(s::ZBasisState, ::QuantumOpticsRepr) = (_l0,_l1)[s.idx] -express_nolookup(s::FockState, r::QuantumOpticsRepr) = fockstate(FockBasis(r.fock_cutoff),s.idx) -express_nolookup(s::CoherentState, r::QuantumOpticsRepr) = coherentstate(FockBasis(r.fock_cutoff),s.alpha) -express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(FockBasis(r.fock_cutoff)) -express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(FockBasis(r.fock_cutoff)) -express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(FockBasis(r.fock_cutoff)) -express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) = displace(FockBasis(r.fock_cutoff), o.alpha) +express_nolookup(s::FockState, r::QuantumOpticsRepr) = fockstate(FockBasis(r.cutoff),s.idx) +express_nolookup(s::CoherentState, r::QuantumOpticsRepr) = coherentstate(FockBasis(r.cutoff),s.alpha) +express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(FockBasis(r.cutoff)) +express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(FockBasis(r.cutoff)) +express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(FockBasis(r.cutoff)) +express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) = displace(FockBasis(r.cutoff), o.alpha) express_nolookup(x::MixedState, ::QuantumOpticsRepr) = identityoperator(basis(x))/length(basis(x)) # TODO there is probably a more efficient way to represent it -express_nolookup(x::IdentityOp, r::QuantumOpticsRepr) = identityoperator(FockBasis(r.fock_cutoff)) +express_nolookup(x::IdentityOp, r::QuantumOpticsRepr) = identityoperator(FockBasis(r.cutoff)) express_nolookup(p::PauliNoiseCPTP, ::QuantumOpticsRepr) = LazySuperSum(SpinBasis(1//2), [1-p.px-p.py-p.pz,p.px,p.py,p.pz], [LazyPrePost(_id,_id),LazyPrePost(_x,_x),LazyPrePost(_y,_y),LazyPrePost(_z,_z)]) diff --git a/src/QSymbolicsBase/express.jl b/src/QSymbolicsBase/express.jl index 8e35595..e40b2f0 100644 --- a/src/QSymbolicsBase/express.jl +++ b/src/QSymbolicsBase/express.jl @@ -78,17 +78,9 @@ end ## """Representation using kets, bras, density matrices, and superoperators governed by `QuantumOptics.jl`.""" -struct QuantumOpticsRepr <: AbstractRepresentation - fock_cutoff::Union{Nothing, Int} - function QuantumOpticsRepr(fock_cutoff::Union{Nothing, Int}) - if isnothing(fock_cutoff) - return new(2) - else - return new(fock_cutoff) - end - end +@kwdef struct QuantumOpticsRepr <: AbstractRepresentation + cutoff::Int = 2 end -QuantumOpticsRepr() = QuantumOpticsRepr(nothing) """Similar to `QuantumOpticsRepr`, but using trajectories instead of superoperators.""" struct QuantumMCRepr <: AbstractRepresentation end """Representation using tableaux governed by `QuantumClifford.jl`"""