|
318 | 318 | throw(IncompatibleBases())
|
319 | 319 | end
|
320 | 320 |
|
| 321 | +# TODO should all of PauliTransferMatrix, ChiMatrix, ChoiState, and KrausOperators subclass AbstractSuperOperator? |
| 322 | +""" |
| 323 | + KrausOperators(B1, B2, data) |
| 324 | +
|
| 325 | +Superoperator represented as a list of Kraus operators. |
| 326 | +Note unlike the SuperOperator or ChoiState types where |
| 327 | +its possible to have basis_l[1] != basis_l[2] and basis_r[1] != basis_r[2] |
| 328 | +which allows representations of maps between general linear operators defined on H_A \to H_B, |
| 329 | +a quantum channel can only act on valid density operators which live in H_A \to H_A. |
| 330 | +Thus the Kraus representation is only defined for quantum channels which map |
| 331 | +(H_A \to H_A) \to (H_B \to H_B). |
| 332 | + """ |
| 333 | +mutable struct KrausOperators{B1,B2,T} <: AbstractSuperOperator{B1,B2} |
| 334 | + basis_l::B1 |
| 335 | + basis_r::B2 |
| 336 | + data::T |
| 337 | + function KrausOperators{BL,BR,T}(basis_l::BL, basis_r::BR, data::T) where {BL,BR,T} |
| 338 | + if (any(!samebases(basis_r, M.basis_r) for M in data) || |
| 339 | + any(!samebases(basis_l, M.basis_l) for M in data)) |
| 340 | + throw(DimensionMismatch("Tried to assign data with incompatible bases")) |
| 341 | + end |
| 342 | + |
| 343 | + new(basis_l, basis_r, data) |
| 344 | + end |
| 345 | +end |
| 346 | +KrausOperators{BL,BR}(b1::BL,b2::BR,data::T) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data) |
| 347 | +KrausOperators(b1::BL,b2::BR,data::T) where {BL,BR,T} = KrausOperators{BL,BR,T}(b1,b2,data) |
| 348 | +KrausOperators(b,data) = KrausOperators(b,b,data) |
| 349 | +tensor(a::KrausOperators, b::KrausOperators) = |
| 350 | + KrausOperators(a.basis_l ⊗ b.basis_l, a.basis_r ⊗ b.basis_r, |
| 351 | + [A ⊗ B for A in a.data for B in b.data]) |
| 352 | +dagger(a::KrausOperators) = KrausOperators(a.basis_r, a.basis_l, [dagger(op) for op in a.data]) |
| 353 | +*(a::KrausOperators{B1,B2}, b::KrausOperators{B2,B3}) where {B1,B2,B3} = |
| 354 | + KrausOperators(a.basis_l, b.basis_r, [A*B for A in a.data for B in b.data]) |
| 355 | +*(a::KrausOperators, b::KrausOperators) = throw(IncompatibleBases()) |
| 356 | +*(a::KrausOperators{BL,BR}, b::Operator{BR,BR}) where {BL,BR} = |
| 357 | + sum(op*b*dagger(op) for op in a.data) |
| 358 | + |
| 359 | +function minimize_kraus_rank(kraus; tol=1e-9) |
| 360 | + bl, br = kraus.basis_l, kraus.basis_r |
| 361 | + dim = length(bl)*length(br) |
| 362 | + |
| 363 | + A = stack(reshape(op.data, dim) for op in kraus.data; dims=1) |
| 364 | + F = qr(A; tol=tol) |
| 365 | + rank = maximum(findnz(F.R)[1]) # rank(F) doesn't work but should |
| 366 | + #@assert (F.R ≈ (sparse(F.Q') * A[F.prow,F.pcol])[1:dim,:]) |
| 367 | + #@assert (all(iszero(F.R[rank+1:end,:]))) |
| 368 | + |
| 369 | + ops = [Operator(bl, br, copy(reshape( # copy materializes reshaped view |
| 370 | + F.R[i,invperm(F.pcol)], (length(bl), length(br))))) for i=1:rank] |
| 371 | + return KrausOperators(bl, br, ops) |
| 372 | +end |
| 373 | + |
321 | 374 | """
|
322 | 375 | ChoiState <: AbstractSuperOperator
|
323 | 376 |
|
@@ -376,3 +429,63 @@ SuperOperator(op::ChoiState) = SuperOperator(_super_choi(op.basis_l, op.basis_r,
|
376 | 429 | *(a::ChoiState, b::SuperOperator) = SuperOperator(a)*b
|
377 | 430 | *(a::SuperOperator, b::ChoiState) = a*SuperOperator(b)
|
378 | 431 |
|
| 432 | +SuperOperator(kraus::KrausOperators) = |
| 433 | + SuperOperator((kraus.basis_l, kraus.basis_l), (kraus.basis_r, kraus.basis_r), |
| 434 | + (sum(conj(op)⊗op for op in kraus.data)).data) |
| 435 | + |
| 436 | +ChoiState(kraus::KrausOperators; tol=1e-9) = |
| 437 | + ChoiState((kraus.basis_r, kraus.basis_l), (kraus.basis_r, kraus.basis_l), |
| 438 | + (sum((M=op.data; reshape(M, (length(M), 1))*reshape(M, (1, length(M)))) |
| 439 | + for op in kraus.data)); tol=tol) |
| 440 | + |
| 441 | +function KrausOperators(choi::ChoiState; tol=1e-9) |
| 442 | + if (!samebases(choi.basis_l[1], choi.basis_r[1]) || |
| 443 | + !samebases(choi.basis_l[2], choi.basis_r[2])) |
| 444 | + throw(DimensionMismatch("Tried to convert choi state of something that isn't a quantum channel mapping density operators to density operators")) |
| 445 | + end |
| 446 | + bl, br = choi.basis_l[2], choi.basis_l[1] |
| 447 | + #ishermitian(choi.data) || @warn "ChoiState is not hermitian" |
| 448 | + # TODO: figure out how to do this with sparse matrices using e.g. Arpack.jl or ArnoldiMethod.jl |
| 449 | + vals, vecs = eigen(Hermitian(Matrix(choi.data))) |
| 450 | + for val in vals |
| 451 | + (abs(val) > tol && val < 0) && @warn "eigval $(val) < 0 but abs(eigval) > tol=$(tol)" |
| 452 | + end |
| 453 | + ops = [Operator(bl, br, sqrt(val)*reshape(vecs[:,i], length(bl), length(br))) |
| 454 | + for (i, val) in enumerate(vals) if abs(val) > tol && val > 0] |
| 455 | + return KrausOperators(bl, br, ops) |
| 456 | +end |
| 457 | + |
| 458 | +KrausOperators(op::SuperOperator; tol=1e-9) = KrausOperators(ChoiState(op; tol=tol); tol=tol) |
| 459 | + |
| 460 | +*(a::ChoiState, b::SuperOperator) = SuperOperator(a)*b |
| 461 | +*(a::SuperOperator, b::ChoiState) = a*SuperOperator(b) |
| 462 | +*(a::KrausOperators, b::SuperOperator) = SuperOperator(a)*b |
| 463 | +*(a::SuperOperator, b::KrausOperators) = a*SuperOperator(b) |
| 464 | +*(a::KrausOperators, b::ChoiState) = SuperOperator(a)*SuperOperator(b) |
| 465 | +*(a::ChoiState, b::KrausOperators) = SuperOperator(a)*SuperOperator(b) |
| 466 | + |
| 467 | +function is_trace_preserving(kraus::KrausOperators; tol=1e-9) |
| 468 | + m = I(length(kraus.basis_r)) - sum(dagger(M)*M for M in kraus.data).data |
| 469 | + m[abs.(m) .< tol] .= 0 |
| 470 | + return iszero(m) |
| 471 | +end |
| 472 | + |
| 473 | +# this check seems suspect... since it fails while the below check on choi succeeeds |
| 474 | +#function is_valid_channel(kraus::KrausOperators; tol=1e-9) |
| 475 | +# m = I(length(kraus.basis_r)) - sum(dagger(M)*M for M in kraus.data).data |
| 476 | +# eigs = eigvals(Matrix(m)) |
| 477 | +# eigs[@. abs(eigs) < tol || eigs > 0] .= 0 |
| 478 | +# return iszero(eigs) |
| 479 | +#end |
| 480 | + |
| 481 | +#function is_valid_channel(choi::ChoiState; tol=1e-9) |
| 482 | +# eigs = eigvals(Hermitian(Matrix(choi.data))) |
| 483 | +# eigs[@. abs(eigs) < tol || eigs > 0] .= 0 |
| 484 | +# return iszero(eigs) |
| 485 | +#end |
| 486 | + |
| 487 | +function is_valid_channel(choi::ChoiState; tol=1e-9) |
| 488 | + any(abs.(choi.data - choi.data') .> tol) && return false |
| 489 | + eigs = eigvals(Hermitian(Matrix(choi.data))) |
| 490 | + return all(@. abs(eigs) < tol || eigs > 0) |
| 491 | +end |
0 commit comments