Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further development of Fock states #69

Merged
merged 16 commits into from
Aug 6, 2024
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
Expand Down
147 changes: 147 additions & 0 deletions docs/src/fock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Fock States
apkille marked this conversation as resolved.
Show resolved Hide resolved

```@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.
apkille marked this conversation as resolved.
Show resolved Hide resolved

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:
apkille marked this conversation as resolved.
Show resolved Hide resolved

```jldoctest
julia> b = FockBasis(Inf, 0.0)
Fock(cutoff=Inf)

julia> f = FockState(3, b)
|3⟩
```
apkille marked this conversation as resolved.
Show resolved Hide resolved

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⟩
apkille marked this conversation as resolved.
Show resolved Hide resolved

julia> lower = Destroy*f
a|3⟩

julia> qsimplify(lower, rewriter=qsimplify_fock)
1.7320508075688772|2⟩
apkille marked this conversation as resolved.
Show resolved Hide resolved
```
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⟩
apkille marked this conversation as resolved.
Show resolved Hide resolved
```
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);
apkille marked this conversation as resolved.
Show resolved Hide resolved

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
```
66 changes: 53 additions & 13 deletions ext/QuantumOpticsExt/QuantumOpticsExt.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
module QuantumOpticsExt

using QuantumInterface, QuantumOpticsBase
using QuantumInterface: samebases
using QuantumSymbolics
using QuantumSymbolics:
HGate, XGate, YGate, ZGate, CPHASEGate, CNOTGate, PauliP, PauliM,
XCXGate, XCYGate, XCZGate, YCXGate, YCYGate, YCZGate, ZCXGate, ZCYGate, ZCZGate,
XBasisState, YBasisState, ZBasisState,
NumberOp, CreateOp, DestroyOp,
FockBasisState,
apkille marked this conversation as resolved.
Show resolved Hide resolved
FockState,
MixedState, IdentityOp,
qubit_basis, inf_fock_basis
import QuantumSymbolics: express, express_nolookup
Expand Down Expand Up @@ -70,28 +71,67 @@
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)
@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]
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
apkille marked this conversation as resolved.
Show resolved Hide resolved
function express_nolookup(s::CoherentState, r::QuantumOpticsRepr)
b = basis(s)
alpha = s.alpha
if !samebases(b, inf_fock_basis)
coherentstate(b, alpha)

Check warning on line 88 in ext/QuantumOpticsExt/QuantumOpticsExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumOpticsExt/QuantumOpticsExt.jl#L88

Added line #L88 was not covered by tests
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)
@warn "Fock space cutoff is not specified so we default to 2"
return _n₂
b = basis(o)
if !samebases(b, inf_fock_basis)
number(b)

Check warning on line 97 in ext/QuantumOpticsExt/QuantumOpticsExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumOpticsExt/QuantumOpticsExt.jl#L97

Added line #L97 was not covered by tests
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)
@warn "Fock space cutoff is not specified so we default to 2"
return _ad₂
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)
@warn "Fock space cutoff is not specified so we default to 2"
return _a₂
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)

Check warning on line 125 in ext/QuantumOpticsExt/QuantumOpticsExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/QuantumOpticsExt/QuantumOpticsExt.jl#L125

Added line #L125 was not covered by tests
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"
apkille marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
8 changes: 4 additions & 4 deletions src/QSymbolicsBase/QSymbolicsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ 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,
NumberOp,CreateOp,DestroyOp,
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,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock,
qexpand,
isunitary

Expand Down Expand Up @@ -142,7 +142,6 @@ end
Base.isequal(::SymQObj, ::Symbolic{Complex}) = false
Base.isequal(::Symbolic{Complex}, ::SymQObj) = false


# TODO check that this does not cause incredibly bad runtime performance
# use a macro to provide specializations if that is indeed the case
propsequal(x,y) = all(n->(n==:metadata || isequal(getproperty(x,n),getproperty(y,n))), propertynames(x))
Expand All @@ -164,6 +163,7 @@ include("basic_ops_inhomogeneous.jl")
include("linalg.jl")
include("predefined.jl")
include("predefined_CPTP.jl")
include("predefined_fock.jl")

##
# Symbolic and simplification rules
Expand Down
48 changes: 0 additions & 48 deletions src/QSymbolicsBase/predefined.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 FockBasisState <: SpecialKet
idx::Int
basis::Basis
end
symbollabel(x::FockBasisState) = "$(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
Expand All @@ -69,13 +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 = FockBasisState(0,inf_fock_basis)
"""Single photon basis state of n"""
const F₁ = const F1 = FockBasisState(1,inf_fock_basis)


##
# Gates and Operators on qubits
##
Expand Down Expand Up @@ -173,29 +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(::AbstractSingleBosonGate) = inf_fock_basis

@withmetadata struct NumberOp <: AbstractSingleBosonOp end
symbollabel(::NumberOp) = "n"
@withmetadata struct CreateOp <: AbstractSingleBosonOp end
symbollabel(::CreateOp) = "a†"
@withmetadata struct DestroyOp <: AbstractSingleBosonOp end
symbollabel(::DestroyOp) = "a"

"""Number operator, also available as the constant `n̂`"""
const N = const n̂ = NumberOp()
"""Creation operator, also available as the constant `âꜛ` - there is no unicode dagger superscript, so we use the uparrow"""
const Create = const âꜛ = CreateOp()
"""Annihilation operator, also available as the constant `â`"""
const Destroy = const â = DestroyOp()

##
# Other special or useful objects
##
Expand Down
Loading
Loading