Skip to content

Commit 85aed5f

Browse files
apkilleKrastanov
andauthored
Add new symbolic bosonic operators and states (#113)
* add new QHO states * add error messages and tests * make error messages appear on a single line * change names of EPRState and ThermalState * parametrize .photons field with Number * update changelog and project.toml to v0.4.11-dev * use curried method of `isequal` in rules.jl * simplify wrt basis fields * mark unitary operations as gates, not ops * fix typing issue * fix more typing issues * rm Fock basis args in test_express_opt.jl --------- Co-authored-by: Stefan Krastanov <[email protected]>
1 parent 8f5d571 commit 85aed5f

File tree

7 files changed

+91
-70
lines changed

7 files changed

+91
-70
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# News
22

3+
## v0.4.11 - dev
4+
5+
- Add new symbolic bosonic states and operators (`TwoSqueezeOp`, `TwoSqueezedState`, `BosonicThermalState`, `BeamSplitterOp`).
6+
37
## v0.4.10 - 2025-05-11
48

59
- Polish `Base.show` methods for application products and scaled quantum objects.

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "QuantumSymbolics"
22
uuid = "efa7fd63-0460-4890-beb7-be1bbdfbaeae"
33
authors = ["QuantumSymbolics.jl contributors"]
4-
version = "0.4.10"
4+
version = "0.4.11-dev"
55

66
[deps]
77
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"

src/QSymbolicsBase/QSymbolicsBase.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ export SymQObj,QObj,
4343
MixedState,IdentityOp,
4444
SApplyKet,SApplyBra,SMulOperator,SSuperOpApply,SCommutator,SAnticommutator,SBraKet,SOuterKetBra,
4545
HGate,XGate,YGate,ZGate,CPHASEGate,CNOTGate,
46-
XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,
46+
XBasisState,YBasisState,ZBasisState,FockState,CoherentState,SqueezedState,TwoSqueezedState,BosonicThermalState,
4747
NumberOp,CreateOp,DestroyOp,PhaseShiftOp,DisplaceOp,SqueezeOp,
48+
TwoSqueezeOp,BeamSplitterOp,
4849
XCXGate,XCYGate,XCZGate,YCXGate,YCYGate,YCZGate,ZCXGate,ZCYGate,ZCZGate,
4950
qsimplify,qsimplify_pauli,qsimplify_commutator,qsimplify_anticommutator,qsimplify_fock,
5051
qexpand,

src/QSymbolicsBase/predefined_fock.jl

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,54 @@
22
# Predefined objects in the Fock space.
33
##
44

5+
const inf_fock_basis = FockBasis(Inf,0.0) # Inf is Float, so the second parameter has to be Float too
6+
7+
abstract type AbstractSingleBosonState <: SpecialKet end
8+
abstract type AbstractTwoBosonState <: SpecialKet end
9+
basis(::AbstractSingleBosonState) = inf_fock_basis
10+
basis(::AbstractTwoBosonState) = inf_fock_basis^2
11+
512
"""Fock state in defined Fock basis."""
6-
@withmetadata struct FockState <: SpecialKet
13+
@withmetadata struct FockState <: AbstractSingleBosonState
714
idx::Int
8-
basis::FockBasis
915
end
10-
FockState(idx::Int) = FockState(idx, inf_fock_basis)
1116
symbollabel(x::FockState) = "$(x.idx)"
1217

1318
"""Coherent state in defined Fock basis."""
14-
@withmetadata struct CoherentState <: SpecialKet
19+
@withmetadata struct CoherentState <: AbstractSingleBosonState
1520
alpha::Number # TODO parameterize
16-
basis::FockBasis
1721
end
18-
CoherentState(alpha::Number) = CoherentState(alpha, inf_fock_basis)
1922
symbollabel(x::CoherentState) = "$(x.alpha)"
2023

2124
"""Squeezed vacuum state in defined Fock basis."""
22-
@withmetadata struct SqueezedState <: SpecialKet
25+
@withmetadata struct SqueezedState <: AbstractSingleBosonState
2326
z::Number
24-
basis::FockBasis
2527
end
26-
SqueezedState(z::Number) = SqueezedState(z, inf_fock_basis)
2728
symbollabel(x::SqueezedState) = "0,$(x.z)"
2829

29-
const inf_fock_basis = FockBasis(Inf,0.)
30-
"""Vacuum basis state of n"""
30+
"""Two-mode squeezed vacuum state, or EPR state, in defined Fock basis."""
31+
@withmetadata struct TwoSqueezedState <: AbstractTwoBosonState
32+
z::Number
33+
end
34+
symbollabel(x::TwoSqueezedState) = "0,$(x.z)"
35+
36+
"""Single-mode vacuum state"""
3137
const vac = const F₀ = const F0 = FockState(0)
32-
"""Single photon basis state of n"""
38+
"""Single photon state"""
3339
const F₁ = const F1 = FockState(1)
3440

3541
##
3642
# Gates and Operators on harmonic oscillators
3743
##
3844

3945
abstract type AbstractSingleBosonOp <: Symbolic{AbstractOperator} end
46+
abstract type AbstractTwoBosonOp <: Symbolic{AbstractOperator} end
4047
abstract type AbstractSingleBosonGate <: AbstractSingleBosonOp end # TODO maybe an IsUnitaryTrait is a better choice
48+
abstract type AbstractTwoBosonGate <: AbstractTwoBosonOp end
4149
isexpr(::AbstractSingleBosonGate) = false
4250
basis(x::AbstractSingleBosonOp) = inf_fock_basis
51+
isexpr(::AbstractTwoBosonGate) = false
52+
basis(x::AbstractTwoBosonOp) = inf_fock_basis^2
4353

4454
"""Number operator.
4555
@@ -54,10 +64,7 @@ julia> qsimplify(num*f, rewriter=qsimplify_fock)
5464
2|2⟩
5565
```
5666
"""
57-
@withmetadata struct NumberOp <: AbstractSingleBosonOp
58-
basis::FockBasis
59-
end
60-
NumberOp() = NumberOp(inf_fock_basis)
67+
@withmetadata struct NumberOp <: AbstractSingleBosonOp end
6168
symbollabel(::NumberOp) = "n"
6269

6370
"""Creation (raising) operator.
@@ -73,10 +80,7 @@ julia> qsimplify(create*f, rewriter=qsimplify_fock)
7380
(sqrt(3))|3⟩
7481
```
7582
"""
76-
@withmetadata struct CreateOp <: AbstractSingleBosonOp
77-
basis::FockBasis
78-
end
79-
CreateOp() = CreateOp(inf_fock_basis)
83+
@withmetadata struct CreateOp <: AbstractSingleBosonOp end
8084
symbollabel(::CreateOp) = "a†"
8185

8286
"""Annihilation (lowering or destroy) operator in defined Fock basis.
@@ -92,10 +96,7 @@ julia> qsimplify(destroy*f, rewriter=qsimplify_fock)
9296
(sqrt(2))|1⟩
9397
```
9498
"""
95-
@withmetadata struct DestroyOp <: AbstractSingleBosonOp
96-
basis::FockBasis
97-
end
98-
DestroyOp() = DestroyOp(inf_fock_basis)
99+
@withmetadata struct DestroyOp <: AbstractSingleBosonOp end
99100
symbollabel(::DestroyOp) = "a"
100101

101102
"""Phase-shift operator in defined Fock basis.
@@ -111,11 +112,9 @@ julia> qsimplify(phase*c, rewriter=qsimplify_fock)
111112
|1.2246467991473532e-16 - 1.0im⟩
112113
```
113114
"""
114-
@withmetadata struct PhaseShiftOp <: AbstractSingleBosonOp
115+
@withmetadata struct PhaseShiftOp <: AbstractSingleBosonGate
115116
phase::Number
116-
basis::FockBasis
117117
end
118-
PhaseShiftOp(phase::Number) = PhaseShiftOp(phase, inf_fock_basis)
119118
symbollabel(x::PhaseShiftOp) = "U($(x.phase))"
120119

121120
"""Displacement operator in defined Fock basis.
@@ -131,11 +130,9 @@ julia> qsimplify(displace*f, rewriter=qsimplify_fock)
131130
|im⟩
132131
```
133132
"""
134-
@withmetadata struct DisplaceOp <: AbstractSingleBosonOp
133+
@withmetadata struct DisplaceOp <: AbstractSingleBosonGate
135134
alpha::Number
136-
basis::FockBasis
137135
end
138-
DisplaceOp(alpha::Number) = DisplaceOp(alpha, inf_fock_basis)
139136
symbollabel(x::DisplaceOp) = "D($(x.alpha))"
140137

141138
"""Number operator, also available as the constant `n̂`, in an infinite dimension Fock basis."""
@@ -156,9 +153,25 @@ julia> qsimplify(S*vac, rewriter=qsimplify_fock)
156153
|0,π⟩
157154
```
158155
"""
159-
@withmetadata struct SqueezeOp <: AbstractSingleBosonOp
156+
@withmetadata struct SqueezeOp <: AbstractSingleBosonGate
160157
z::Number
161-
basis::FockBasis
162158
end
163-
SqueezeOp(z::Number) = SqueezeOp(z, inf_fock_basis)
164-
symbollabel(x::SqueezeOp) = "S($(x.z))"
159+
symbollabel(x::SqueezeOp) = "S($(x.z))"
160+
161+
"""Thermal bosonic state in defined Fock basis."""
162+
@withmetadata struct BosonicThermalState <: AbstractSingleBosonOp
163+
photons::Number
164+
end
165+
symbollabel(x::BosonicThermalState) = "ρₜₕ($(x.photons))"
166+
167+
"""Two-mode squeezing operator in defined Fock basis."""
168+
@withmetadata struct TwoSqueezeOp <: AbstractTwoBosonGate
169+
z::Number
170+
end
171+
symbollabel(x::TwoSqueezeOp) = "S₂($(x.z))"
172+
173+
"""Two-mode beamsplitter operator in defined Fock basis."""
174+
@withmetadata struct BeamSplitterOp <: AbstractTwoBosonGate
175+
transmit::Number
176+
end
177+
symbollabel(x::BeamSplitterOp) = "B($(x.transmit))"

src/QSymbolicsBase/rules.jl

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ function hasscalings(xs)
1111
end
1212
end
1313
_isa(T) = x->isa(x,T)
14-
_isequal(obj) = x->(x==obj)
1514
_vecisa(T) = x->all(_isa(T), x)
1615

1716
##
@@ -33,36 +32,36 @@ RULES_PAULI = [
3332
@rule(~o1::_isa(HGate)*~o2::_isa(YGate)*~o3::_isa(HGate) => -Y),
3433
@rule(~o1::_isa(HGate)*~o2::_isa(ZGate)*~o3::_isa(HGate) => X),
3534

36-
@rule(~o::_isa(XGate)*~k::_isequal(X1) => X1),
37-
@rule(~o::_isa(YGate)*~k::_isequal(X1) => -im*X2),
38-
@rule(~o::_isa(ZGate)*~k::_isequal(X1) => X2),
35+
@rule(~o::_isa(XGate)*~k::isequal(X1) => X1),
36+
@rule(~o::_isa(YGate)*~k::isequal(X1) => -im*X2),
37+
@rule(~o::_isa(ZGate)*~k::isequal(X1) => X2),
3938

40-
@rule(~o::_isa(XGate)*~k::_isequal(X2) => -X2),
41-
@rule(~o::_isa(YGate)*~k::_isequal(X2) => im*X1),
42-
@rule(~o::_isa(ZGate)*~k::_isequal(X2) => X1),
39+
@rule(~o::_isa(XGate)*~k::isequal(X2) => -X2),
40+
@rule(~o::_isa(YGate)*~k::isequal(X2) => im*X1),
41+
@rule(~o::_isa(ZGate)*~k::isequal(X2) => X1),
4342

44-
@rule(~o::_isa(XGate)*~k::_isequal(Y1) => im*Y2),
45-
@rule(~o::_isa(YGate)*~k::_isequal(Y1) => Y1),
46-
@rule(~o::_isa(ZGate)*~k::_isequal(Y1) => Y2),
43+
@rule(~o::_isa(XGate)*~k::isequal(Y1) => im*Y2),
44+
@rule(~o::_isa(YGate)*~k::isequal(Y1) => Y1),
45+
@rule(~o::_isa(ZGate)*~k::isequal(Y1) => Y2),
4746

48-
@rule(~o::_isa(XGate)*~k::_isequal(Y2) => -im*Y1),
49-
@rule(~o::_isa(YGate)*~k::_isequal(Y2) => -Y2),
50-
@rule(~o::_isa(ZGate)*~k::_isequal(Y2) => Y1),
47+
@rule(~o::_isa(XGate)*~k::isequal(Y2) => -im*Y1),
48+
@rule(~o::_isa(YGate)*~k::isequal(Y2) => -Y2),
49+
@rule(~o::_isa(ZGate)*~k::isequal(Y2) => Y1),
5150

52-
@rule(~o::_isa(XGate)*~k::_isequal(Z1) => Z2),
53-
@rule(~o::_isa(YGate)*~k::_isequal(Z1) => im*Z2),
54-
@rule(~o::_isa(ZGate)*~k::_isequal(Z1) => Z1),
51+
@rule(~o::_isa(XGate)*~k::isequal(Z1) => Z2),
52+
@rule(~o::_isa(YGate)*~k::isequal(Z1) => im*Z2),
53+
@rule(~o::_isa(ZGate)*~k::isequal(Z1) => Z1),
5554

56-
@rule(~o::_isa(XGate)*~k::_isequal(Z2) => Z1),
57-
@rule(~o::_isa(YGate)*~k::_isequal(Z2) => -im*Z1),
58-
@rule(~o::_isa(ZGate)*~k::_isequal(Z2) => -Z2),
55+
@rule(~o::_isa(XGate)*~k::isequal(Z2) => Z1),
56+
@rule(~o::_isa(YGate)*~k::isequal(Z2) => -im*Z1),
57+
@rule(~o::_isa(ZGate)*~k::isequal(Z2) => -Z2),
5958

60-
@rule(~o::_isa(HGate)*~k::_isequal(X1) => Z1),
61-
@rule(~o::_isa(HGate)*~k::_isequal(X2) => Z2),
62-
@rule(~o::_isa(HGate)*~k::_isequal(Y1) => (X1+im*X2)/sqrt(2)),
63-
@rule(~o::_isa(HGate)*~k::_isequal(Y2) => (X1-im*X2)/sqrt(2)),
64-
@rule(~o::_isa(HGate)*~k::_isequal(Z1) => X1),
65-
@rule(~o::_isa(HGate)*~k::_isequal(Z2) => X2)
59+
@rule(~o::_isa(HGate)*~k::isequal(X1) => Z1),
60+
@rule(~o::_isa(HGate)*~k::isequal(X2) => Z2),
61+
@rule(~o::_isa(HGate)*~k::isequal(Y1) => (X1+im*X2)/sqrt(2)),
62+
@rule(~o::_isa(HGate)*~k::isequal(Y2) => (X1-im*X2)/sqrt(2)),
63+
@rule(~o::_isa(HGate)*~k::isequal(Z1) => X1),
64+
@rule(~o::_isa(HGate)*~k::isequal(Z2) => X2)
6665
]
6766

6867
# Commutator identities
@@ -86,27 +85,28 @@ RULES_ANTICOMMUTATOR = [
8685
@rule(anticommutator(~o1::_isa(YGate), ~o2::_isa(XGate)) => 0),
8786
@rule(anticommutator(~o1::_isa(ZGate), ~o2::_isa(YGate)) => 0),
8887
@rule(anticommutator(~o1::_isa(XGate), ~o2::_isa(ZGate)) => 0),
89-
@rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp((~o1).basis)),
90-
@rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp((~o1).basis)),
88+
@rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(CreateOp)) => IdentityOp(basis(~o1))),
89+
@rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(DestroyOp)) => -IdentityOp(basis(~o1))),
9190
@rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(DestroyOp)) => -(~o2)),
9291
@rule(commutator(~o1::_isa(DestroyOp), ~o2::_isa(NumberOp)) => (~o1)),
9392
@rule(commutator(~o1::_isa(NumberOp), ~o2::_isa(CreateOp)) => (~o2)),
9493
@rule(commutator(~o1::_isa(CreateOp), ~o2::_isa(NumberOp)) => -(~o1))
9594
]
9695

9796
RULES_FOCK = [
98-
@rule(~o::_isa(DestroyOp) * ~k::_isequal(vac) => SZeroKet()),
97+
@rule(~o::_isa(DestroyOp) * ~k::isequal(vac) => SZeroKet()),
9998
@rule(~o::_isa(CreateOp) * ~k::_isa(FockState) => Term(sqrt,[(~k).idx+1])*FockState((~k).idx+1)),
10099
@rule(~o::_isa(DestroyOp) * ~k::_isa(FockState) => Term(sqrt,[(~k).idx])*FockState((~k).idx-1)),
101100
@rule(~o::_isa(NumberOp) * ~k::_isa(FockState) => (~k).idx*(~k)),
102101
@rule(~o::_isa(DestroyOp) * ~k::_isa(CoherentState) => (~k).alpha*(~k)),
103102
@rule(~o::_isa(PhaseShiftOp) * ~k::_isa(CoherentState) => CoherentState((~k).alpha * exp(-im*(~o).phase))),
104103
@rule(dagger(~o1::_isa(PhaseShiftOp)) * ~o2::_isa(DestroyOp) * ~o1 => ~o2*exp(-im*((~o1).phase))),
105104
@rule(~o1::_isa(PhaseShiftOp) * ~o2::_isa(DestroyOp) * dagger(~o1) => ~o2*exp(im*((~o1).phase))),
106-
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp((~o2).basis)),
107-
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp((~o2).basis)),
105+
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(DestroyOp) * ~o1 => (~o2) + (~o1).alpha*IdentityOp(basis(~o2))),
106+
@rule(dagger(~o1::_isa(DisplaceOp)) * ~o2::_isa(CreateOp) * ~o1 => (~o2) + conj((~o1).alpha)*IdentityOp(basis(~o2))),
108107
@rule(~o::_isa(DisplaceOp) * ~k::((x->(isa(x,FockState) && x.idx == 0))) => CoherentState((~o).alpha)),
109-
@rule(~o::_isa(SqueezeOp) * ~k::_isequal(vac) => SqueezedState((~o).z, (~o).basis))
108+
@rule(~o::_isa(SqueezeOp) * ~k::isequal(vac) => SqueezedState((~o).z)),
109+
@rule(~o::_isa(TwoSqueezeOp) * ~k::isequal(vac vac) => TwoSqueezedState((~o).z))
110110
]
111111

112112
RULES_SIMPLIFY = [RULES_PAULI; RULES_COMMUTATOR; RULES_ANTICOMMUTATOR; RULES_FOCK]

test/test_express_opt.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636

3737
state = (3im*(2*dagger(Z1)+dagger(Y1))) * (3im*(2*X1+X2))
3838

39-
cstate = CoherentState(im, inf_fock_basis)
40-
displace = DisplaceOp(im,inf_fock_basis)
41-
phase = PhaseShiftOp(im, inf_fock_basis)
39+
cstate = CoherentState(im)
40+
displace = DisplaceOp(im)
41+
phase = PhaseShiftOp(im)
4242
@test express(N*F1) express(N)*express(F1)
4343
@test express(Create*F1) express(Create)*express(F1)
4444
@test express(Destroy*F1) express(Destroy)*express(F1)

test/test_fock.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
phase2 = PhaseShiftOp(pi)
88
displace = DisplaceOp(im)
99
squeezeop = SqueezeOp(pi)
10+
twosqueezeop = TwoSqueezeOp(pi)
1011
sstate = SqueezedState(pi)
12+
tsstate = TwoSqueezedState(pi)
1113

1214
@testset "ladder and number operators" begin
1315
@test isequal(qsimplify(Destroy*vac, rewriter=qsimplify_fock), SZeroKet())
@@ -28,5 +30,6 @@
2830

2931
@testset "Squeeze operators" begin
3032
@test isequal(qsimplify(squeezeop*vac, rewriter=qsimplify_fock), sstate)
33+
@test isequal(qsimplify(twosqueezeop*(vac vac), rewriter=qsimplify_fock), tsstate)
3134
end
3235
end

0 commit comments

Comments
 (0)