Skip to content

Commit 2b6763b

Browse files
akirakyleKrastanov
andauthored
Create ChoiState type and conversions to/from SuperOperator (#115)
Co-authored-by: Stefan Krastanov <[email protected]>
1 parent d6aadeb commit 2b6763b

File tree

4 files changed

+103
-4
lines changed

4 files changed

+103
-4
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "QuantumOpticsBase"
22
uuid = "4f57444f-1401-5e15-980d-4471b28d5678"
3-
version = "0.5.3"
3+
version = "0.5.4"
44

55
[deps]
66
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"

src/superoperators.jl

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ mutable struct SuperOperator{B1,B2,T} <: AbstractSuperOperator{B1,B2}
1111
basis_r::B2
1212
data::T
1313
function SuperOperator{BL,BR,T}(basis_l::BL, basis_r::BR, data::T) where {BL,BR,T}
14-
if length(basis_l[1])*length(basis_l[2]) != size(data, 1) ||
15-
length(basis_r[1])*length(basis_r[2]) != size(data, 2)
14+
if (length(basis_l) != 2 || length(basis_r) != 2 ||
15+
length(basis_l[1])*length(basis_l[2]) != size(data, 1) ||
16+
length(basis_r[1])*length(basis_r[2]) != size(data, 2))
1617
throw(DimensionMismatch("Tried to assign data of size $(size(data)) to Hilbert spaces of sizes $(length.(basis_l)), $(length.(basis_r))"))
1718
end
1819
new(basis_l, basis_r, data)
@@ -316,3 +317,61 @@ end
316317
}
317318
throw(IncompatibleBases())
318319
end
320+
321+
"""
322+
ChoiState <: AbstractSuperOperator
323+
324+
Superoperator represented as a choi state.
325+
"""
326+
mutable struct ChoiState{B1,B2,T} <: AbstractSuperOperator{B1,B2}
327+
basis_l::B1
328+
basis_r::B2
329+
data::T
330+
function ChoiState{BL,BR,T}(basis_l::BL, basis_r::BR, data::T) where {BL,BR,T}
331+
if (length(basis_l) != 2 || length(basis_r) != 2 ||
332+
length(basis_l[1])*length(basis_l[2]) != size(data, 1) ||
333+
length(basis_r[1])*length(basis_r[2]) != size(data, 2))
334+
throw(DimensionMismatch("Tried to assign data of size $(size(data)) to Hilbert spaces of sizes $(length.(basis_l)), $(length.(basis_r))"))
335+
end
336+
new(basis_l, basis_r, data)
337+
end
338+
end
339+
ChoiState(b1::BL, b2::BR, data::T) where {BL,BR,T} = ChoiState{BL,BR,T}(b1, b2, data)
340+
341+
dense(a::ChoiState) = ChoiState(a.basis_l, a.basis_r, Matrix(a.data))
342+
sparse(a::ChoiState) = ChoiState(a.basis_l, a.basis_r, sparse(a.data))
343+
dagger(a::ChoiState) = ChoiState(dagger(SuperOperator(a)))
344+
*(a::ChoiState, b::ChoiState) = ChoiState(SuperOperator(a)*SuperOperator(b))
345+
*(a::ChoiState, b::Operator) = SuperOperator(a)*b
346+
==(a::ChoiState, b::ChoiState) = (SuperOperator(a) == SuperOperator(b))
347+
348+
# reshape swaps within systems due to colum major ordering
349+
# https://docs.qojulia.org/quantumobjects/operators/#tensor_order
350+
function _super_choi((l1, l2), (r1, r2), data)
351+
data = reshape(data, map(length, (l2, l1, r2, r1)))
352+
(l1, l2), (r1, r2) = (r2, l2), (r1, l1)
353+
data = permutedims(data, (1, 3, 2, 4))
354+
data = reshape(data, map(length, (l1l2, r1r2)))
355+
return (l1, l2), (r1, r2), data
356+
end
357+
358+
function _super_choi((r2, l2), (r1, l1), data::SparseMatrixCSC)
359+
data = _permutedims(data, map(length, (l2, r2, l1, r1)), (1, 3, 2, 4))
360+
data = reshape(data, map(length, (l1l2, r1r2)))
361+
# sparse(data) is necessary since reshape of a sparse array returns a
362+
# ReshapedSparseArray which is not a subtype of AbstractArray and so
363+
# _permutedims fails to acces the ".m" field
364+
# https://github.com/qojulia/QuantumOpticsBase.jl/pull/83
365+
# https://github.com/JuliaSparse/SparseArrays.jl/issues/24
366+
# permutedims in SparseArrays.jl only implements perm (2,1) and so
367+
# _permutedims should probably be upstreamed
368+
# https://github.com/JuliaLang/julia/issues/26534
369+
return (l1, l2), (r1, r2), sparse(data)
370+
end
371+
372+
ChoiState(op::SuperOperator) = ChoiState(_super_choi(op.basis_l, op.basis_r, op.data)...)
373+
SuperOperator(op::ChoiState) = SuperOperator(_super_choi(op.basis_l, op.basis_r, op.data)...)
374+
375+
*(a::ChoiState, b::SuperOperator) = SuperOperator(a)*b
376+
*(a::SuperOperator, b::ChoiState) = a*SuperOperator(b)
377+

test/test_superoperators.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
using Test
22
using QuantumOpticsBase
33
using SparseArrays, LinearAlgebra
4+
import QuantumOpticsBase: ChoiState # Remove when ChoiState is publicly exported
45

56
@testset "superoperators" begin
67

78
# Test creation
89
b = GenericBasis(3)
910
@test_throws DimensionMismatch DenseSuperOperator((b, b), (b, b), zeros(ComplexF64, 3, 3))
1011
@test_throws DimensionMismatch SparseSuperOperator((b, b), (b, b), spzeros(ComplexF64, 3, 3))
12+
@test_throws DimensionMismatch ChoiState((b, b), (b, b), zeros(ComplexF64, 3, 3))
1113

1214
# Test copy, sparse and dense
1315
b1 = GenericBasis(2)
@@ -153,29 +155,45 @@ J = [Ja, Jc]
153155
ρ₀ = dm(Ψ₀)
154156

155157
@test identitysuperoperator(spinbasis)*sx == sx
158+
@test ChoiState(identitysuperoperator(spinbasis))*sx == sx
156159
@test identitysuperoperator(sparse(spre(sx)))*sx == sparse(sx)
160+
@test ChoiState(identitysuperoperator(sparse(spre(sx))))*sx == sparse(sx)
161+
@test sparse(ChoiState(identitysuperoperator(spre(sx))))*sx == sparse(sx)
157162
@test identitysuperoperator(dense(spre(sx)))*sx == dense(sx)
163+
@test ChoiState(identitysuperoperator(dense(spre(sx))))*sx == dense(sx)
164+
@test dense(ChoiState(identitysuperoperator(spre(sx))))*sx == dense(sx)
158165

159166
op1 = DenseOperator(spinbasis, [1.2+0.3im 0.7+1.2im;0.3+0.1im 0.8+3.2im])
160167
op2 = DenseOperator(spinbasis, [0.2+0.1im 0.1+2.3im; 0.8+4.0im 0.3+1.4im])
161168
@test tracedistance(spre(op1)*op2, op1*op2) < 1e-12
169+
@test tracedistance(ChoiState(spre(op1))*op2, op1*op2) < 1e-12
162170
@test tracedistance(spost(op1)*op2, op2*op1) < 1e-12
171+
@test tracedistance(ChoiState(spost(op1))*op2, op2*op1) < 1e-12
163172

164173
@test spre(sparse(op1))*op2 == op1*op2
174+
@test ChoiState(spre(sparse(op1)))*op2 == op1*op2
165175
@test spost(sparse(op1))*op2 == op2*op1
176+
@test ChoiState(spost(sparse(op1)))*op2 == op2*op1
166177
@test spre(sparse(dagger(op1)))*op2 == dagger(op1)*op2
178+
@test ChoiState(spre(sparse(dagger(op1))))*op2 == dagger(op1)*op2
167179
@test spre(dense(dagger(op1)))*op2 dagger(op1)*op2
180+
@test ChoiState(spre(dense(dagger(op1))))*op2 dagger(op1)*op2
168181
@test sprepost(sparse(op1), op1)*op2 op1*op2*op1
182+
@test ChoiState(sprepost(sparse(op1), op1))*op2 op1*op2*op1
169183

170184
@test spre(sparse(op1))*sparse(op2) == sparse(op1*op2)
185+
@test ChoiState(spre(sparse(op1)))*sparse(op2) == sparse(op1*op2)
171186
@test spost(sparse(op1))*sparse(op2) == sparse(op2*op1)
187+
@test ChoiState(spost(sparse(op1)))*sparse(op2) == sparse(op2*op1)
172188
@test sprepost(sparse(op1), sparse(op1))*sparse(op2) sparse(op1*op2*op1)
189+
@test ChoiState(sprepost(sparse(op1), sparse(op1)))*sparse(op2) sparse(op1*op2*op1)
173190

174191
@test sprepost(op1, op2) spre(op1)*spost(op2)
175192
b1 = FockBasis(1)
176193
b2 = FockBasis(5)
177194
op = fockstate(b1, 0) dagger(fockstate(b2, 0))
178195
@test sprepost(dagger(op), op)*dm(fockstate(b1, 0)) == dm(fockstate(b2, 0))
196+
@test ChoiState(sprepost(dagger(op), op))*dm(fockstate(b1, 0)) == dm(fockstate(b2, 0))
179197
@test_throws ArgumentError spre(op)
180198
@test_throws ArgumentError spost(op)
181199

@@ -185,12 +203,14 @@ for j=J
185203
ρ .+= j*ρ₀*dagger(j) - 0.5*dagger(j)*j*ρ₀ - 0.5*ρ₀*dagger(j)*j
186204
end
187205
@test tracedistance(L*ρ₀, ρ) < 1e-10
206+
@test tracedistance(ChoiState(L)*ρ₀, ρ) < 1e-10
188207

189208
# tout, ρt = timeevolution.master([0.,1.], ρ₀, H, J; reltol=1e-7)
190209
# @test tracedistance(exp(dense(L))*ρ₀, ρt[end]) < 1e-6
191210
# @test tracedistance(exp(sparse(L))*ρ₀, ρt[end]) < 1e-6
192211

193212
@test dense(spre(op1)) == spre(op1)
213+
@test dense(ChoiState(spre(op1))) == ChoiState(spre(op1))
194214

195215
@test L/2.0 == 0.5*L == L*0.5
196216
@test -L == SparseSuperOperator(L.basis_l, L.basis_r, -L.data)
@@ -228,4 +248,20 @@ N = exp(log(2) * sparse(L)) # 50% loss channel
228248
@test (0.5 - real(tr^2))) < 1e-10 # one photon state becomes maximally mixed
229249
@test tracedistance(ρ, normalize(dm(fockstate(b, 0)) + dm(fockstate(b, 1)))) < 1e-10
230250

251+
# Testing 0-2-4 binomial code encoder
252+
b_logical = SpinBasis(1//2)
253+
b_fock = FockBasis(5)
254+
z_l = normalize(fockstate(b_fock, 0) + fockstate(b_fock, 4))
255+
o_l = fockstate(b_fock, 2)
256+
encoder_kraus = z_l dagger(spinup(b_logical)) + o_l dagger(spindown(b_logical))
257+
encoder_sup = sprepost(encoder_kraus, dagger(encoder_kraus))
258+
decoder_sup = sprepost(dagger(encoder_kraus), encoder_kraus)
259+
@test SuperOperator(ChoiState(encoder_sup)).data == encoder_sup.data
260+
@test decoder_sup == dagger(encoder_sup)
261+
@test ChoiState(decoder_sup) == dagger(ChoiState(encoder_sup))
262+
@test decoder_sup*encoder_sup dense(identitysuperoperator(b_logical))
263+
@test decoder_sup*ChoiState(encoder_sup) dense(identitysuperoperator(b_logical))
264+
@test ChoiState(decoder_sup)*encoder_sup dense(identitysuperoperator(b_logical))
265+
@test SuperOperator(ChoiState(decoder_sup)*ChoiState(encoder_sup)) dense(identitysuperoperator(b_logical))
266+
231267
end # testset

test/test_time_dependent_operators.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,11 @@ using LinearAlgebra, Random
103103
o_t_tup = TimeDependentSum(Tuple, o_t)
104104
@test QOB.static_operator(o_t_tup(t)).factors == [1.0im, t*3.0 + 0.0im]
105105
@test all(QOB.static_operator(o_t_tup(t)).operators .== (a, n))
106-
@test (@allocated set_time!(o_t_tup, t)) == 0
106+
if VERSION.minor == 11 # issue #178 https://github.com/qojulia/QuantumOpticsBase.jl/issues/178
107+
@test_broken (@allocated set_time!(o_t_tup, t)) == 0
108+
else
109+
@test (@allocated set_time!(o_t_tup, t)) == 0
110+
end
107111

108112
o_t2 = TimeDependentSum(f1=>a, f2=>n)
109113
@test o_t(t) == o_t2(t)

0 commit comments

Comments
 (0)