Skip to content

Commit 9f36a02

Browse files
committed
Add make_trace_preserving and rework docs for orthogonalize and canonicalize
1 parent 30f832a commit 9f36a02

File tree

2 files changed

+47
-25
lines changed

2 files changed

+47
-25
lines changed

src/QuantumOpticsBase.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export Basis, GenericBasis, CompositeBasis, basis,
3737
#superoperators
3838
SuperOperator, DenseSuperOperator, DenseSuperOpType,
3939
SparseSuperOperator, SparseSuperOpType, ChoiState, KrausOperators,
40-
canonicalize, orthogonalize, is_cptp, is_cptni,
40+
canonicalize, orthogonalize, make_trace_preserving, is_cptp, is_cptni,
4141
is_completely_positive, is_trace_preserving, is_trace_nonincreasing,
4242
spre, spost, sprepost, liouvillian, identitysuperoperator,
4343
#fock

src/superoperators.jl

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,8 @@ Thus the Kraus representation is only defined for quantum channels which map
412412
mutable struct KrausOperators{B1,B2,T} <: AbstractSuperOperator{B1,B2}
413413
basis_l::B1
414414
basis_r::B2
415-
data::T
416-
function KrausOperators{BL,BR,T}(basis_l::BL, basis_r::BR, data::T) where {BL,BR,T}
415+
data::Vector{T}
416+
function KrausOperators{BL,BR,T}(basis_l::BL, basis_r::BR, data::Vector{T}) where {BL,BR,T}
417417
if (any(!samebases(basis_r, M.basis_r) for M in data) ||
418418
any(!samebases(basis_l, M.basis_l) for M in data))
419419
throw(DimensionMismatch("Tried to assign data with incompatible bases"))
@@ -422,8 +422,8 @@ mutable struct KrausOperators{B1,B2,T} <: AbstractSuperOperator{B1,B2}
422422
new(basis_l, basis_r, data)
423423
end
424424
end
425-
KrausOperators{BL,BR}(b1::BL,b2::BR,data::T) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data)
426-
KrausOperators(b1::BL,b2::BR,data::T) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data)
425+
KrausOperators{BL,BR}(b1::BL,b2::BR,data::Vector{T}) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data)
426+
KrausOperators(b1::BL,b2::BR,data::Vector{T}) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data)
427427

428428
tensor(a::KrausOperators, b::KrausOperators) =
429429
KrausOperators(a.basis_l b.basis_l, a.basis_r b.basis_r,
@@ -437,20 +437,19 @@ dagger(a::KrausOperators) = KrausOperators(a.basis_r, a.basis_l, [dagger(op) for
437437
isapprox(a::KrausOperators, b::KrausOperators; kwargs...) = isapprox(SuperOperator(a), SuperOperator(b); kwargs...)
438438

439439
"""
440-
canonicalize(kraus::KrausOperators; tol=1e-12)
440+
orthogonalize(kraus::KrausOperators; tol=1e-12)
441441
442-
Canonicalize the set kraus operators by performing a qr decomposition.
443-
A quantum channel with kraus operators ``{A_k}`` is in cannonical form if and only if
444-
445-
```math
446-
\\Tr A_i^\\dagger A_j \\sim \\delta_{i,j}
447-
```
442+
Orthogonalize the set kraus operators by performing a qr decomposition on their vec'd operators.
443+
Note that this is different than `canonicalize` which returns a kraus decomposition such
444+
that the kraus operators are Hilbert–Schmidt orthorgonal.
448445
449446
If the input dimension is d and output dimension is d' then the number of kraus
450-
operators returned is guaranteed to be no greater than dd' and will furthermore
451-
be equal the Kraus rank of the channel up to numerical imprecision controlled by `tol`.
447+
operators returned is guaranteed to be no greater than dd', however it may be greater
448+
than the Kraus rank.
449+
450+
`orthogonalize` should always be much faster than canonicalize as it avoids an explicit eigendecomposition.
452451
"""
453-
function canonicalize(kraus::KrausOperators; tol=1e-12)
452+
function orthogonalize(kraus::KrausOperators; tol=1e-12)
454453
bl, br = kraus.basis_l, kraus.basis_r
455454
dim = length(bl)*length(br)
456455

@@ -467,8 +466,31 @@ function canonicalize(kraus::KrausOperators; tol=1e-12)
467466
return KrausOperators(bl, br, ops)
468467
end
469468

470-
# TODO: check if canonicalize and orthogonalize are equivalent
471-
orthogonalize(kraus::KrausOperators; tol=1e-12) = KrausOperators(ChoiState(kraus); tol=tol)
469+
"""
470+
canonicalize(kraus::KrausOperators; tol=1e-12)
471+
472+
Transform the quantum channel into canonical form such that the kraus operators ``{A_k}``
473+
are Hilbert–Schmidt orthorgonal:
474+
475+
```math
476+
\\Tr A_i^\\dagger A_j \\sim \\delta_{i,j}
477+
```
478+
479+
If the input dimension is d and output dimension is d' then the number of kraus
480+
operators returned is guaranteed to be no greater than dd' and will furthermore
481+
be equal the Kraus rank of the channel up to numerical imprecision controlled by `tol`.
482+
"""
483+
canonicalize(kraus::KrausOperators; tol=1e-12) = KrausOperators(ChoiState(kraus); tol=tol)
484+
485+
# TODO: document
486+
function make_trace_preserving(kraus; tol=1e-12)
487+
m = I - sum(dagger(M)*M for M in kraus.data).data
488+
if isa(_positive_eigen(m; tol=tol), Number)
489+
throw(ArgumentError("Channel must be trace nonincreasing"))
490+
end
491+
K = Operator(kraus.basis_l, kraus.basis_r, sqrt(Matrix(m)))
492+
return KrausOperators(kraus.basis_l, kraus.basis_r, [kraus.data; K])
493+
end
472494

473495
SuperOperator(kraus::KrausOperators) =
474496
SuperOperator((kraus.basis_l, kraus.basis_l), (kraus.basis_r, kraus.basis_r),
@@ -536,18 +558,18 @@ function is_completely_positive(choi::ChoiState; tol=1e-12)
536558
return true
537559
end
538560

539-
is_completely_positive(super::SuperOperator; tol=1e-12) = is_completely_positive(ChoiState(super))
561+
is_completely_positive(super::SuperOperator; tol=1e-12) =
562+
is_completely_positive(ChoiState(super); tol=tol)
540563

541564
# TODO: document this
542565
is_trace_preserving(kraus::KrausOperators; tol=1e-12) =
543566
_is_identity(sum(dagger(M)*M for M in kraus.data).data, tol=tol)
544567

545-
function is_trace_preserving(choi::ChoiState; tol=1e-12)
546-
is_completely_positive(choi; tol=tol) || return false
547-
return _is_identity(ptrace(choi_to_operator(choi), 1).data, tol=tol)
548-
end
568+
is_trace_preserving(choi::ChoiState; tol=1e-12) =
569+
_is_identity(ptrace(choi_to_operator(choi), 1).data, tol=tol)
549570

550-
is_trace_preserving(super::SuperOperator; tol=1e-12) = is_trace_preserving(ChoiState(super); tol=tol)
571+
is_trace_preserving(super::SuperOperator; tol=1e-12) =
572+
is_trace_preserving(ChoiState(super); tol=tol)
551573

552574
# TODO: document this
553575
function is_trace_nonincreasing(kraus::KrausOperators; tol=1e-12)
@@ -556,12 +578,12 @@ function is_trace_nonincreasing(kraus::KrausOperators; tol=1e-12)
556578
end
557579

558580
function is_trace_nonincreasing(choi::ChoiState; tol=1e-12)
559-
is_completely_positive(choi; tol=tol) || return false
560581
m = I - ptrace(choi_to_operator(choi), 1).data
561582
return !isa(_positive_eigen(m; tol=tol), Number)
562583
end
563584

564-
is_trace_nonincreasing(super::SuperOperator; tol=1e-12) = is_trace_nonincreasing(ChoiState(super); tol=tol)
585+
is_trace_nonincreasing(super::SuperOperator; tol=1e-12) =
586+
is_trace_nonincreasing(ChoiState(super); tol=tol)
565587

566588
# TODO: document this
567589
is_cptp(sop; tol=1e-12) = is_completely_positive(sop; tol=tol) && is_trace_preserving(sop; tol=tol)

0 commit comments

Comments
 (0)