Skip to content

Commit

Permalink
implement squeezing
Browse files Browse the repository at this point in the history
  • Loading branch information
apkille committed Aug 24, 2024
1 parent 6275f8b commit 57faf25
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 12 deletions.
25 changes: 17 additions & 8 deletions docs/src/QHO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -31,9 +31,15 @@ julia> c = CoherentState(im)
!!! note "Naming convention for quantum harmonic oscillator bases"
The defined basis for arbitrary symbolic bosonic states is a `FockBasis` object, due to a shared naming interface for Quantum physics packages. For instance, the command `basis(CoherentState(im))` will output `Fock(cutoff=Inf)`. This may lead to confusion, as not all bosonic states are Fock states. However, this is simply a naming convention for the basis, and symbolic and numerical results are not affected by it.

Summarized below are supported bosonic states.

- Fock state: `FockState(idx::Int)`,
- Coherent state: `CoherentState(alpha::Number)`,
- Squeezed coherent state: `SqueezedCoherentState(alpha::Number, z::Number)`.

## Operators

Operations on bosonic states are supported, and can be simplified with `qsimplify` and its rewriter `qsimplify_fock`. For instance, we can apply the raising (creation) $\hat{a}^{\dagger}$ and lowering (annihilation or destroy) $\hat{a}$ operators on a Fock state as follows:
Operations on bosonic states are supported, and can be simplified with [`qsimplify`](@ref) and its rewriter `qsimplify_fock`. For instance, we can apply the raising (creation) $\hat{a}^{\dagger}$ and lowering (annihilation or destroy) $\hat{a}$ operators on a Fock state as follows:

```jldoctest
julia> f = FockState(3);
Expand Down Expand Up @@ -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
Expand All @@ -85,19 +93,20 @@ 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.

- Number operator: `NumberOp()`,
- 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);
Expand Down Expand Up @@ -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:

Expand Down
2 changes: 2 additions & 0 deletions ext/QuantumOpticsExt/QuantumOpticsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,12 @@ function finite_basis(s,r)
end
express_nolookup(s::FockState, r::QuantumOpticsRepr) = fockstate(finite_basis(s,r),s.idx)
express_nolookup(s::CoherentState, r::QuantumOpticsRepr) = coherentstate(finite_basis(s,r),s.alpha)
express_nolookup(s::SqueezedCoherentState, r::QuantumOpticsRepr) = (b = finite_basis(s,r); squeeze(b, s.z)*coherentstate(b, s.alpha))
express_nolookup(o::NumberOp, r::QuantumOpticsRepr) = number(finite_basis(o,r))
express_nolookup(o::CreateOp, r::QuantumOpticsRepr) = create(finite_basis(o,r))
express_nolookup(o::DestroyOp, r::QuantumOpticsRepr) = destroy(finite_basis(o,r))
express_nolookup(o::DisplaceOp, r::QuantumOpticsRepr) = displace(finite_basis(o,r), o.alpha)
express_nolookup(o::SqueezeOp, r::QuantumOpticsRepr) = squeeze(finite_basis(o,r), o.z)
express_nolookup(x::MixedState, r::QuantumOpticsRepr) = identityoperator(finite_basis(x,r))/length(finite_basis(x,r)) # TODO there is probably a more efficient way to represent it
express_nolookup(x::IdentityOp, r::QuantumOpticsRepr) = identityoperator(finite_basis(x,r))

Expand Down
4 changes: 2 additions & 2 deletions src/QSymbolicsBase/QSymbolicsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export SymQObj,QObj,
MixedState,IdentityOp,
SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra,
HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate,
XBasisState,YBasisState,ZBasisState,FockState,CoherentState,
NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,
XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedCoherentState,
NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp,
XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate,
qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock,
qexpand,
Expand Down
31 changes: 30 additions & 1 deletion src/QSymbolicsBase/predefined_fock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ end
CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis)
symbollabel(x::CoherentState) = "$(x.alpha)"

"""Squeezed coherent state in defined Fock basis."""
@withmetadata struct SqueezedCoherentState <: SpecialKet
alpha::Number
z::Number
basis::FockBasis
end
SqueezedCoherentState(alpha::Number, z::Number) = SqueezedCoherentState(alpha, z, inf_fock_basis)
symbollabel(x::SqueezedCoherentState) = "$(x.alpha),$(x.z)"

const inf_fock_basis = FockBasis(Inf,0.)
"""Vacuum basis state of n"""
const vac = const F₀ = const F0 = FockState(0)
Expand Down Expand Up @@ -136,4 +145,24 @@ const N = const n̂ = NumberOp()
There is no unicode dagger superscript, so we use the uparrow"""
const Create = const âꜛ = CreateOp()
"""Annihilation operator, also available as the constant `â`, in an infinite dimension Fock basis."""
const Destroy = const= DestroyOp()
const Destroy = const= DestroyOp()

"""Squeezing operator in defined Fock basis.
```jldoctest
julia> c = CoherentState(im)
|im⟩
julia> S = SqueezeOp(pi)
S(π)
julia> qsimplify(S*c, rewriter=qsimplify_fock)
|im,π⟩
```
"""
@withmetadata struct SqueezeOp <: AbstractSingleBosonOp
z::Number
basis::FockBasis
end
SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis)
symbollabel(x::SqueezeOp) = "S($(x.z))"
3 changes: 2 additions & 1 deletion src/QSymbolicsBase/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ RULES_FOCK = [
@rule(~o1::_isa(PhaseShiftOp) * ~o2::_isa(DestroyOp) * dagger(~o1) => ~o2*exp(im*((~o1).phase))),
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)),
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)),
@rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha))
@rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)),
@rule(~o::_isa(SqueezeOp) * ~k::_isa(CoherentState) => SqueezedCoherentState((~k).alpha, (~o).z))
]

RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK]
Expand Down
4 changes: 4 additions & 0 deletions test/test_express_opt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@
@test express(Create*F1) express(Create)*express(F1)
@test express(Destroy*F1) express(Destroy)*express(F1)
@test express(displace*cstate) express(displace)*express(cstate)

squeezedcstate = SqueezedCoherentState(im, pi)
squeezeop = SqueezeOp(pi)
@test express(squeezedcstate) express(squeezeop)*express(cstate)
end
6 changes: 6 additions & 0 deletions test/test_fock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
phase1 = PhaseShiftOp(0)
phase2 = PhaseShiftOp(pi)
displace = DisplaceOp(im)
squeezeop = SqueezeOp(pi)
scstate = SqueezedCoherentState(im,pi)

@testset "ladder and number operators" begin
@test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet())
Expand All @@ -23,4 +25,8 @@
@test isequal(qsimplify(dagger(displace)*Create*displace, rewriter=qsimplify_fock), Create - im*IdentityOp(inf_fock_basis))
@test isequal(qsimplify(displace*vac, rewriter=qsimplify_fock), cstate)
end

@testset "Squeeze operators" begin
@test isequal(qsimplify(squeezeop*cstate, rewriter=qsimplify_fock), scstate)
end
end

0 comments on commit 57faf25

Please sign in to comment.