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

Add super_to_choi and choi_to_super #115

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/QuantumOpticsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ export Basis, GenericBasis, CompositeBasis, basis,
current_time, time_shift, time_stretch, time_restrict, static_operator,
#superoperators
SuperOperator, DenseSuperOperator, DenseSuperOpType,
SparseSuperOperator, SparseSuperOpType, spre, spost, sprepost, liouvillian,
identitysuperoperator,
SparseSuperOperator, SparseSuperOpType,
ChoiState, KrausOperators, is_trace_preserving, is_valid_channel,
spre, spost, sprepost, liouvillian, identitysuperoperator,
#fock
FockBasis, number, destroy, create,
fockstate, coherentstate, coherentstate!,
Expand Down
128 changes: 126 additions & 2 deletions src/superoperators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
basis_r::B2
data::T
function SuperOperator{BL,BR,T}(basis_l::BL, basis_r::BR, data::T) where {BL,BR,T}
if length(basis_l[1])*length(basis_l[2]) != size(data, 1) ||
length(basis_r[1])*length(basis_r[2]) != size(data, 2)
if (length(basis_l) != 2 || length(basis_r) != 2 ||
length(basis_l[1])*length(basis_l[2]) != size(data, 1) ||
length(basis_r[1])*length(basis_r[2]) != size(data, 2))
throw(DimensionMismatch("Tried to assign data of size $(size(data)) to Hilbert spaces of sizes $(length.(basis_l)), $(length.(basis_r))"))
end
new(basis_l, basis_r, data)
Expand Down Expand Up @@ -316,3 +317,126 @@
}
throw(IncompatibleBases())
end

# TODO should all of PauliTransferMatrix, ChiMatrix, ChoiState, and KrausOperators subclass AbstractSuperOperator?

"""
KrausOperators(B1, B2, data)

Superoperator represented as a list of Kraus operators.
Note unlike the SuperOperator or ChoiState types where
its possible to have basis_l[1] != basis_l[2] and basis_r[1] != basis_r[2]
which allows representations of maps between general linear operators defined on H_A \to H_B,
a quantum channel can only act on valid density operators which live in H_A \to H_A.
Thus the Kraus representation is only defined for quantum channels which map
(H_A \to H_A) \to (H_B \to H_B).
"""
mutable struct KrausOperators{B1,B2,T} <: AbstractSuperOperator{B1,B2}
basis_l::B1
basis_r::B2
data::T
function KrausOperators{BL,BR,T}(basis_l::BL, basis_r::BR, data::T) where {BL,BR,T}
if (any(!samebases(basis_r, M.basis_r) for M in data) ||

Check warning on line 339 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L338-L339

Added lines #L338 - L339 were not covered by tests
any(!samebases(basis_l, M.basis_l) for M in data))
throw(DimensionMismatch("Tried to assign data with incompatible bases"))

Check warning on line 341 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L341

Added line #L341 was not covered by tests
end

new(basis_l, basis_r, data)

Check warning on line 344 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L344

Added line #L344 was not covered by tests
end
end
KrausOperators{BL,BR}(b1::BL,b2::BR,data::T) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data)
KrausOperators(b1::BL,b2::BR,data::T) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data)
KrausOperators(b,data) = KrausOperators(b,b,data)

Check warning on line 349 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L347-L349

Added lines #L347 - L349 were not covered by tests

function is_trace_preserving(kraus::KrausOperators; tol=1e-9)
m = I(length(kraus.basis_r)) - sum(dagger(M)*M for M in kraus.data).data
m[abs.(m) .< tol] .= 0
return iszero(m)

Check warning on line 354 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L351-L354

Added lines #L351 - L354 were not covered by tests
end

function is_valid_channel(kraus::KrausOperators; tol=1e-9)
m = I(length(kraus.basis_r)) - sum(dagger(M)*M for M in kraus.data).data
eigs = eigvals(Matrix(m))
eigs[@. abs(eigs) < tol || eigs > 0] .= 0
return iszero(eigs)

Check warning on line 361 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L357-L361

Added lines #L357 - L361 were not covered by tests
end

"""
ChoiState(B1, B2, data)

Superoperator represented as a choi state stored as a sparse or dense matrix.
"""
mutable struct ChoiState{B1,B2,T} <: AbstractSuperOperator{B1,B2}
basis_l::B1
basis_r::B2
data::T
function ChoiState{BL,BR,T}(basis_l::BL, basis_r::BR, data::T; tol=1e-9) where {BL,BR,T}
if (length(basis_l) != 2 || length(basis_r) != 2 ||

Check warning on line 374 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L373-L374

Added lines #L373 - L374 were not covered by tests
length(basis_l[1])*length(basis_l[2]) != size(data, 1) ||
length(basis_r[1])*length(basis_r[2]) != size(data, 2))
throw(DimensionMismatch("Tried to assign data of size $(size(data)) to Hilbert spaces of sizes $(length.(basis_l)), $(length.(basis_r))"))

Check warning on line 377 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L377

Added line #L377 was not covered by tests
end
if any(abs.(data - data') .> tol)
@warn "Trying to construct ChoiState from non-hermitian data"

Check warning on line 380 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L379-L380

Added lines #L379 - L380 were not covered by tests
end
new(basis_l, basis_r, Hermitian(data))

Check warning on line 382 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L382

Added line #L382 was not covered by tests
end
end
ChoiState{BL,BR}(b1::BL,b2::BR,data::T; tol=1e-9) where {BL,BR,T} = ChoiState{BL,BR,T}(b1,b2,data;tol=tol)
ChoiState(b1::BL,b2::BR,data::T; tol=1e-9) where {BL,BR,T} = ChoiState{BL,BR,T}(b1,b2,data; tol=tol)
ChoiState(b,data; tol=tol) = ChoiState(b,b,data; tol=tol)

Check warning on line 387 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L385-L387

Added lines #L385 - L387 were not covered by tests

# TODO: document why we have super_to_choi return non-trace one density matrices.
# https://forest-benchmarking.readthedocs.io/en/latest/superoperator_representations.html
# Note the similarity to permutesystems in operators_dense.jl

# reshape swaps within systems due to colum major ordering
# https://docs.qojulia.org/quantumobjects/operators/#tensor_order
function _super_choi((l1, l2), (r1, r2), data::Matrix)
data = reshape(data, map(length, (l2, l1, r2, r1)))
(l1, l2), (r1, r2) = (r2, l2), (r1, l1)
data = permutedims(data, (1, 3, 2, 4))
data = reshape(data, map(length, (l1⊗l2, r1⊗r2)))
return (l1, l2), (r1, r2), data

Check warning on line 400 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L395-L400

Added lines #L395 - L400 were not covered by tests
end

function _super_choi((r2, l2), (r1, l1), data::SparseMatrixCSC)
data = _permutedims(data, map(length, (l2, r2, l1, r1)), (1, 3, 2, 4))
data = reshape(data, map(length, (l1⊗l2, r1⊗r2)))

Check warning on line 405 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L403-L405

Added lines #L403 - L405 were not covered by tests
# sparse(data) is necessary since reshape of a sparse array returns a
# ReshapedSparseArray which is not a subtype of AbstractArray and so
# _permutedims fails to acces the ".m" field
# https://github.com/qojulia/QuantumOpticsBase.jl/pull/83
# https://github.com/JuliaSparse/SparseArrays.jl/issues/24
# permutedims in SparseArrays.jl only implements perm (2,1) and so
# _permutedims should be upstreamed
# https://github.com/JuliaLang/julia/issues/26534
return (l1, l2), (r1, r2), sparse(data)

Check warning on line 414 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L414

Added line #L414 was not covered by tests
end

ChoiState(op::SuperOperator; tol=1e-9) = ChoiState(_super_choi(op.basis_l, op.basis_r, op.data)...; tol=tol)
SuperOperator(kraus::KrausOperators) =

Check warning on line 418 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L417-L418

Added lines #L417 - L418 were not covered by tests
SuperOperator((kraus.basis_l, kraus.basis_l), (kraus.basis_r, kraus.basis_r),
(sum(conj(op)⊗op for op in kraus.data)).data)

SuperOperator(op::ChoiState) = SuperOperator(_super_choi(op.basis_l, op.basis_r, op.data)...)
ChoiState(kraus::KrausOperators) = ChoiState(SuperOperator(kraus))

Check warning on line 423 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L422-L423

Added lines #L422 - L423 were not covered by tests

function KrausOperators(choi::ChoiState; tol=1e-9)
if (!samebases(choi.basis_l[1], choi.basis_l[2]) ||

Check warning on line 426 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L425-L426

Added lines #L425 - L426 were not covered by tests
!samebases(choi.basis_r[1], choi.basis_r[2]))
throw(DimensionMismatch("Tried to convert choi state of something that isn't a quantum channel mapping density operators to density operators"))

Check warning on line 428 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L428

Added line #L428 was not covered by tests
end
bl, br = choi.basis_l[1], choi.basis_r[1]

Check warning on line 430 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L430

Added line #L430 was not covered by tests
#ishermitian(choi.data) || @warn "ChoiState is not hermitian"
# TODO: figure out how to do this with sparse matrices using e.g. Arpack.jl or ArnoldiMethod.jl
vals, vecs = eigen(Hermitian(Matrix(choi.data)))
for val in vals
(abs(val) > tol && val < 0) && @warn "eigval $(val) < 0 but abs(eigval) > tol=$(tol)"
end
ops = [Operator(bl, br, sqrt(val)*reshape(vecs[:,i], length(bl), length(br)))

Check warning on line 437 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L433-L437

Added lines #L433 - L437 were not covered by tests
for (i, val) in enumerate(vals) if abs(val) > tol && val > 0]
return KrausOperators(bl, br, ops)

Check warning on line 439 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L439

Added line #L439 was not covered by tests
end

KrausOperators(op::SuperOperator; tol=1e-9) = KrausOperators(ChoiState(op; tol=tol); tol=tol)

Check warning on line 442 in src/superoperators.jl

View check run for this annotation

Codecov / codecov/patch

src/superoperators.jl#L442

Added line #L442 was not covered by tests
Loading