Skip to content

Commit 2ab7770

Browse files
committed
Create ChoiState type and conversions to/from SuperOperator
1 parent d6aadeb commit 2ab7770

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

src/QuantumOpticsBase.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ export Basis, GenericBasis, CompositeBasis, basis,
3636
current_time, time_shift, time_stretch, time_restrict, static_operator,
3737
#superoperators
3838
SuperOperator, DenseSuperOperator, DenseSuperOpType,
39-
SparseSuperOperator, SparseSuperOpType, spre, spost, sprepost, liouvillian,
40-
identitysuperoperator,
39+
SparseSuperOperator, SparseSuperOpType, ChoiState,
40+
spre, spost, sprepost, liouvillian, identitysuperoperator,
4141
#fock
4242
FockBasis, number, destroy, create,
4343
fockstate, coherentstate, coherentstate!,

src/superoperators.jl

Lines changed: 63 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,63 @@ 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{BL,BR}(b1::BL, b2::BR, data::T) where {BL,BR,T} = ChoiState{BL,BR,T}(b1, b2, data)
340+
ChoiState(b1::BL, b2::BR, data::T) where {BL,BR,T} = ChoiState{BL,BR,T}(b1, b2, data)
341+
ChoiState(b,data) = ChoiState(b, b, data)
342+
343+
dense(a::ChoiState) = ChoiState(a.basis_l, a.basis_r, Matrix(a.data))
344+
sparse(a::ChoiState) = ChoiStae(a.basis_l, a.basis_r, sparse(a.data))
345+
dagger(a::ChoiState) = ChoiState(dagger(SuperOperator(a)))
346+
*(a::ChoiState, b::ChoiState) = ChoiState(SuperOperator(a)*SuperOperator(b))
347+
*(a::ChoiState, b::Operator) = SuperOperator(a)*b
348+
==(a::ChoiState, b::ChoiState) = SuperOperator(a) == SuperOperator(b)
349+
350+
# reshape swaps within systems due to colum major ordering
351+
# https://docs.qojulia.org/quantumobjects/operators/#tensor_order
352+
function _super_choi((l1, l2), (r1, r2), data)
353+
data = reshape(data, map(length, (l2, l1, r2, r1)))
354+
(l1, l2), (r1, r2) = (r2, l2), (r1, l1)
355+
data = permutedims(data, (1, 3, 2, 4))
356+
data = reshape(data, map(length, (l1l2, r1r2)))
357+
return (l1, l2), (r1, r2), data
358+
end
359+
360+
function _super_choi((r2, l2), (r1, l1), data::SparseMatrixCSC)
361+
data = _permutedims(data, map(length, (l2, r2, l1, r1)), (1, 3, 2, 4))
362+
data = reshape(data, map(length, (l1l2, r1r2)))
363+
# sparse(data) is necessary since reshape of a sparse array returns a
364+
# ReshapedSparseArray which is not a subtype of AbstractArray and so
365+
# _permutedims fails to acces the ".m" field
366+
# https://github.com/qojulia/QuantumOpticsBase.jl/pull/83
367+
# https://github.com/JuliaSparse/SparseArrays.jl/issues/24
368+
# permutedims in SparseArrays.jl only implements perm (2,1) and so
369+
# _permutedims should probably be upstreamed
370+
# https://github.com/JuliaLang/julia/issues/26534
371+
return (l1, l2), (r1, r2), sparse(data)
372+
end
373+
374+
ChoiState(op::SuperOperator) = ChoiState(_super_choi(op.basis_l, op.basis_r, op.data)...)
375+
SuperOperator(op::ChoiState) = SuperOperator(_super_choi(op.basis_l, op.basis_r, op.data)...)
376+
377+
*(a::ChoiState, b::SuperOperator) = SuperOperator(a)*b
378+
*(a::SuperOperator, b::ChoiState) = a*SuperOperator(b)
379+

test/test_superoperators.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,29 +153,43 @@ J = [Ja, Jc]
153153
ρ₀ = dm(Ψ₀)
154154

155155
@test identitysuperoperator(spinbasis)*sx == sx
156+
@test ChoiState(identitysuperoperator(spinbasis))*sx == sx
156157
@test identitysuperoperator(sparse(spre(sx)))*sx == sparse(sx)
158+
@test ChoiState(identitysuperoperator(sparse(spre(sx))))*sx == sparse(sx)
157159
@test identitysuperoperator(dense(spre(sx)))*sx == dense(sx)
160+
@test ChoiState(identitysuperoperator(dense(spre(sx))))*sx == dense(sx)
158161

159162
op1 = DenseOperator(spinbasis, [1.2+0.3im 0.7+1.2im;0.3+0.1im 0.8+3.2im])
160163
op2 = DenseOperator(spinbasis, [0.2+0.1im 0.1+2.3im; 0.8+4.0im 0.3+1.4im])
161164
@test tracedistance(spre(op1)*op2, op1*op2) < 1e-12
165+
@test tracedistance(ChoiState(spre(op1))*op2, op1*op2) < 1e-12
162166
@test tracedistance(spost(op1)*op2, op2*op1) < 1e-12
167+
@test tracedistance(ChoiState(spost(op1))*op2, op2*op1) < 1e-12
163168

164169
@test spre(sparse(op1))*op2 == op1*op2
170+
@test ChoiState(spre(sparse(op1)))*op2 == op1*op2
165171
@test spost(sparse(op1))*op2 == op2*op1
172+
@test ChoiState(spost(sparse(op1)))*op2 == op2*op1
166173
@test spre(sparse(dagger(op1)))*op2 == dagger(op1)*op2
174+
@test ChoiState(spre(sparse(dagger(op1))))*op2 == dagger(op1)*op2
167175
@test spre(dense(dagger(op1)))*op2 dagger(op1)*op2
176+
@test ChoiState(spre(dense(dagger(op1))))*op2 dagger(op1)*op2
168177
@test sprepost(sparse(op1), op1)*op2 op1*op2*op1
178+
@test ChoiState(sprepost(sparse(op1), op1))*op2 op1*op2*op1
169179

170180
@test spre(sparse(op1))*sparse(op2) == sparse(op1*op2)
181+
@test ChoiState(spre(sparse(op1)))*sparse(op2) == sparse(op1*op2)
171182
@test spost(sparse(op1))*sparse(op2) == sparse(op2*op1)
183+
@test ChoiState(spost(sparse(op1)))*sparse(op2) == sparse(op2*op1)
172184
@test sprepost(sparse(op1), sparse(op1))*sparse(op2) sparse(op1*op2*op1)
185+
@test ChoiState(sprepost(sparse(op1), sparse(op1)))*sparse(op2) sparse(op1*op2*op1)
173186

174187
@test sprepost(op1, op2) spre(op1)*spost(op2)
175188
b1 = FockBasis(1)
176189
b2 = FockBasis(5)
177190
op = fockstate(b1, 0) dagger(fockstate(b2, 0))
178191
@test sprepost(dagger(op), op)*dm(fockstate(b1, 0)) == dm(fockstate(b2, 0))
192+
@test ChoiState(sprepost(dagger(op), op))*dm(fockstate(b1, 0)) == dm(fockstate(b2, 0))
179193
@test_throws ArgumentError spre(op)
180194
@test_throws ArgumentError spost(op)
181195

@@ -185,12 +199,14 @@ for j=J
185199
ρ .+= j*ρ₀*dagger(j) - 0.5*dagger(j)*j*ρ₀ - 0.5*ρ₀*dagger(j)*j
186200
end
187201
@test tracedistance(L*ρ₀, ρ) < 1e-10
202+
@test tracedistance(ChoiState(L)*ρ₀, ρ) < 1e-10
188203

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

193208
@test dense(spre(op1)) == spre(op1)
209+
@test dense(ChoiState(spre(op1))) == ChoiState(spre(op1))
194210

195211
@test L/2.0 == 0.5*L == L*0.5
196212
@test -L == SparseSuperOperator(L.basis_l, L.basis_r, -L.data)

0 commit comments

Comments
 (0)