Skip to content

Commit c79539f

Browse files
Add missing Gaussian QuantumOpticsExt conversions (#181)
Co-authored-by: Stefan Krastanov <stefan@krastanov.org> Co-authored-by: Stefan Krastanov <github.acc@krastanov.org>
1 parent 9f00067 commit c79539f

File tree

6 files changed

+237
-1
lines changed

6 files changed

+237
-1
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.15 - 2026-03-19
4+
5+
- `QuantumOpticsRepr` support for BosonicThermalState, PhaseShiftOp, BeamSplitterOp, TwoSqueezedState, TwoSqueezeOp
6+
37
## v0.4.14 - 2026-03-03
48

59
- Improvements and regression tests for `tensor` of operators when expressed in `CliffordRepr` (depending on the newly added private `QCGateSequence` in `QuantumCliffordExt`).

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.14"
4+
version = "0.4.15"
55

66
[workspace]
77
projects = ["benchmark", "docs"]

ext/QuantumOpticsExt/QuantumOpticsExt.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ express_nolookup(o::SqueezeOp, r::QuantumOpticsRepr) = squeeze(finite_basis(o,r)
9393
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
9494
express_nolookup(x::IdentityOp, r::QuantumOpticsRepr) = identityoperator(finite_basis(x,r))
9595

96+
include("gaussian.jl")
97+
9698
express_nolookup(p::PauliNoiseCPTP, ::QuantumOpticsRepr) = LazySuperSum(SpinBasis(1//2), [1-p.px-p.py-p.pz,p.px,p.py,p.pz],
9799
[LazyPrePost(_id,_id),LazyPrePost(_x,_x),LazyPrePost(_y,_y),LazyPrePost(_z,_z)])
98100

ext/QuantumOpticsExt/gaussian.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using LinearAlgebra: diagm
2+
3+
finite_two_mode_basis(s, r) = isfinite(length(basis(s))) ? basis(s) : FockBasis(r.cutoff)^2
4+
5+
function _thermalstate(basis, photons)
6+
occupation = photons / (photons + 1)
7+
weights = occupation .^ collect(basis.offset:basis.N)
8+
normalize(DenseOperator(basis, diagm(0 => weights)))
9+
end
10+
11+
function _two_mode_ops(basis)
12+
mode_basis = basis.bases[1]
13+
return (
14+
a1 = embed(basis, 1, destroy(mode_basis)),
15+
a2 = embed(basis, 2, destroy(mode_basis)),
16+
ad1 = embed(basis, 1, create(mode_basis)),
17+
ad2 = embed(basis, 2, create(mode_basis)),
18+
)
19+
end
20+
21+
_phaseshift(basis, phase) = exp(-im * phase * dense(number(basis)))
22+
23+
function _beamsplitter(basis, transmit)
24+
ops = _two_mode_ops(basis)
25+
theta = asin(sqrt(transmit))
26+
exp(theta * dense(ops.ad1 * ops.a2 - ops.a1 * ops.ad2))
27+
end
28+
29+
function _twosqueeze(basis, z)
30+
ops = _two_mode_ops(basis)
31+
exp(dense(conj(z) * ops.a1 * ops.a2 - z * ops.ad1 * ops.ad2))
32+
end
33+
34+
function _two_mode_vacuum(basis)
35+
vacuum = fockstate(basis.bases[1], 0)
36+
vacuum vacuum
37+
end
38+
39+
express_nolookup(s::BosonicThermalState, r::QuantumOpticsRepr) = _thermalstate(finite_basis(s,r), s.photons)
40+
express_nolookup(o::PhaseShiftOp, r::QuantumOpticsRepr) = _phaseshift(finite_basis(o,r), o.phase)
41+
express_nolookup(o::BeamSplitterOp, r::QuantumOpticsRepr) = _beamsplitter(finite_two_mode_basis(o,r), o.transmit)
42+
express_nolookup(s::TwoSqueezedState, r::QuantumOpticsRepr) = (b = finite_two_mode_basis(s,r); _twosqueeze(b, s.z) * _two_mode_vacuum(b))
43+
express_nolookup(o::TwoSqueezeOp, r::QuantumOpticsRepr) = _twosqueeze(finite_two_mode_basis(o,r), o.z)

test/test_gabs_qo_interop.jl

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
@testitem "Gabs and QuantumOptics Gaussian interop" begin
2+
using Gabs
3+
using QuantumOptics
4+
using QuantumSymbolics
5+
6+
samestate(ket1, ket2; atol=1e-10) = isapprox(dm(ket1), dm(ket2); atol=atol, rtol=0)
7+
8+
express_via_gabs(state, repr, basis) = express(express(state, GabsRepr(basis)), repr)
9+
10+
function act_via_gabs(op, state, repr, basis)
11+
grepr = GabsRepr(basis)
12+
express(express(op, grepr) * express(state, grepr), repr)
13+
end
14+
15+
direct_action(op, state, repr) = express(op, repr) * express(state, repr)
16+
symbolic_action(op, state, repr) = express(op * state, repr)
17+
18+
@testset "Pure-state conversions" begin
19+
repr = QuantumOpticsRepr(14)
20+
α = 0.21 - 0.13im
21+
z = 0.17 * exp(-0.3im)
22+
23+
direct_vacuum = express(vac, repr)
24+
direct_product = express(CoherentState(α) vac, repr)
25+
direct_twomode_squeezed = express(TwoSqueezedState(z), repr)
26+
27+
for basis in (QuadPairBasis, QuadBlockBasis)
28+
@test samestate(express_via_gabs(vac, repr, basis), direct_vacuum)
29+
@test samestate(express_via_gabs(CoherentState(α) vac, repr, basis), direct_product)
30+
@test samestate(
31+
express_via_gabs(TwoSqueezedState(z), repr, basis),
32+
direct_twomode_squeezed;
33+
atol=1e-9,
34+
)
35+
end
36+
37+
pair_state = express_via_gabs(TwoSqueezedState(z), repr, QuadPairBasis)
38+
block_state = express_via_gabs(TwoSqueezedState(z), repr, QuadBlockBasis)
39+
@test samestate(pair_state, block_state; atol=1e-9)
40+
end
41+
42+
@testset "Simple edge cases" begin
43+
repr = QuantumOpticsRepr(14)
44+
α = 0.21 - 0.13im
45+
z = 0.0 + 0.2im
46+
πf = float(pi)
47+
48+
for basis in (QuadPairBasis, QuadBlockBasis)
49+
@test samestate(
50+
act_via_gabs(PhaseShiftOp(0.0), CoherentState(α), repr, basis),
51+
express(CoherentState(α), repr),
52+
)
53+
@test samestate(
54+
act_via_gabs(PhaseShiftOp(πf), CoherentState(α), repr, basis),
55+
express(CoherentState(-α), repr),
56+
)
57+
58+
@test samestate(
59+
act_via_gabs(BeamSplitterOp(0.0), CoherentState(α) vac, repr, basis),
60+
express(CoherentState(α) vac, repr),
61+
)
62+
@test samestate(
63+
act_via_gabs(BeamSplitterOp(1.0), CoherentState(α) vac, repr, basis),
64+
express(vac CoherentState(-α), repr),
65+
)
66+
67+
@test samestate(
68+
act_via_gabs(TwoSqueezeOp(0.0 + 0.0im), vac vac, repr, basis),
69+
express(vac vac, repr),
70+
)
71+
@test samestate(
72+
act_via_gabs(TwoSqueezeOp(z), vac vac, repr, basis),
73+
express(TwoSqueezedState(z), repr),
74+
)
75+
end
76+
end
77+
78+
@testset "Operator actions agree across paths" begin
79+
repr = QuantumOpticsRepr(14)
80+
α = 0.21 - 0.13im
81+
z = 0.17 * exp(-0.3im)
82+
83+
phase = PhaseShiftOp(pi / 2)
84+
beamsplitter = BeamSplitterOp(0.35)
85+
twosqueeze = TwoSqueezeOp(z)
86+
87+
phase_state = CoherentState(α)
88+
twomode_state = CoherentState(α) vac
89+
vacuum_pair = vac vac
90+
91+
phase_direct = direct_action(phase, phase_state, repr)
92+
phase_symbolic = symbolic_action(phase, phase_state, repr)
93+
@test samestate(phase_symbolic, phase_direct)
94+
95+
beamsplitter_direct = direct_action(beamsplitter, twomode_state, repr)
96+
beamsplitter_symbolic = symbolic_action(beamsplitter, twomode_state, repr)
97+
@test samestate(beamsplitter_symbolic, beamsplitter_direct)
98+
99+
twosqueeze_direct = direct_action(twosqueeze, twomode_state, repr)
100+
twosqueeze_symbolic = symbolic_action(twosqueeze, twomode_state, repr)
101+
@test samestate(twosqueeze_symbolic, twosqueeze_direct; atol=1e-9)
102+
103+
twosqueeze_vacuum_direct = direct_action(twosqueeze, vacuum_pair, repr)
104+
twosqueeze_vacuum_symbolic = symbolic_action(twosqueeze, vacuum_pair, repr)
105+
twosqueeze_state = express(TwoSqueezedState(z), repr)
106+
@test samestate(twosqueeze_vacuum_symbolic, twosqueeze_vacuum_direct)
107+
@test samestate(twosqueeze_vacuum_symbolic, twosqueeze_state)
108+
109+
for basis in (QuadPairBasis, QuadBlockBasis)
110+
phase_via_gabs = act_via_gabs(phase, phase_state, repr, basis)
111+
@test samestate(phase_via_gabs, phase_direct)
112+
113+
beamsplitter_via_gabs = act_via_gabs(beamsplitter, twomode_state, repr, basis)
114+
@test samestate(beamsplitter_via_gabs, beamsplitter_direct)
115+
116+
twosqueeze_via_gabs = act_via_gabs(twosqueeze, twomode_state, repr, basis)
117+
@test samestate(twosqueeze_via_gabs, twosqueeze_direct; atol=1e-9)
118+
119+
twosqueeze_vacuum_via_gabs = act_via_gabs(twosqueeze, vacuum_pair, repr, basis)
120+
@test samestate(twosqueeze_vacuum_via_gabs, twosqueeze_state; atol=1e-9)
121+
end
122+
123+
pair_state = act_via_gabs(twosqueeze, twomode_state, repr, QuadPairBasis)
124+
block_state = act_via_gabs(twosqueeze, twomode_state, repr, QuadBlockBasis)
125+
@test samestate(pair_state, block_state; atol=1e-9)
126+
end
127+
end

test/test_qo_gaussian.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
@testitem "Test qo gaussian" begin
2+
using LinearAlgebra: diagm
3+
using QuantumOptics
4+
using QuantumSymbolics
5+
6+
samestate(ket1, ket2; atol=1e-10) = isapprox(dm(ket1), dm(ket2); atol=atol, rtol=0)
7+
8+
function two_mode_ops(cutoff)
9+
mode_basis = FockBasis(cutoff)
10+
basis = mode_basis^2
11+
return (
12+
basis = basis,
13+
vacuum = fockstate(mode_basis, 0) fockstate(mode_basis, 0),
14+
a1 = embed(basis, 1, destroy(mode_basis)),
15+
a2 = embed(basis, 2, destroy(mode_basis)),
16+
ad1 = embed(basis, 1, create(mode_basis)),
17+
ad2 = embed(basis, 2, create(mode_basis)),
18+
)
19+
end
20+
21+
@testset "Single-mode Gaussian objects" begin
22+
repr = QuantumOpticsRepr(10)
23+
basis = FockBasis(repr.cutoff)
24+
α = 0.2 - 0.1im
25+
θ = 0.3
26+
= 1.2
27+
28+
thermal = express(BosonicThermalState(n̄), repr)
29+
weights = (n̄ / (n̄ + 1)) .^ collect(basis.offset:basis.N)
30+
expected_thermal = normalize(DenseOperator(basis, diagm(0 => weights)))
31+
32+
@test thermal expected_thermal
33+
@test express(PhaseShiftOp(θ), repr) exp(-im * θ * dense(number(basis)))
34+
@test samestate(
35+
express(PhaseShiftOp(θ) * CoherentState(α), repr),
36+
coherentstate(basis, α * exp(-im * θ)),
37+
)
38+
@test express(PhaseShiftOp(θ) * BosonicThermalState(n̄) * dagger(PhaseShiftOp(θ)), repr) thermal
39+
end
40+
41+
@testset "Two-mode Gaussian objects" begin
42+
repr = QuantumOpticsRepr(8)
43+
ops = two_mode_ops(repr.cutoff)
44+
z = 0.17 * exp(-0.3im)
45+
τ = 0.35
46+
47+
twosqueeze = exp(dense(conj(z) * ops.a1 * ops.a2 - z * ops.ad1 * ops.ad2))
48+
beamsplitter = exp(asin(sqrt(τ)) * dense(ops.ad1 * ops.a2 - ops.a1 * ops.ad2))
49+
50+
@test express(TwoSqueezeOp(z), repr) twosqueeze
51+
@test samestate(express(TwoSqueezedState(z), repr), twosqueeze * ops.vacuum)
52+
@test samestate(express(TwoSqueezeOp(z) * (vac vac), repr), express(TwoSqueezedState(z), repr))
53+
54+
@test express(BeamSplitterOp(τ), repr) beamsplitter
55+
@test samestate(
56+
express(BeamSplitterOp(τ) * (CoherentState(0.1 + 0.2im) vac), repr),
57+
beamsplitter * express(CoherentState(0.1 + 0.2im) vac, repr),
58+
)
59+
end
60+
end

0 commit comments

Comments
 (0)