Skip to content

Commit bbefb39

Browse files
committed
Even more WIP Pauli
1 parent 64d8e01 commit bbefb39

File tree

5 files changed

+88
-77
lines changed

5 files changed

+88
-77
lines changed

src/QuantumOpticsBase.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export Basis, GenericBasis, CompositeBasis, basis, basis_l, basis_r, dimension,
3939
AbstractTimeDependentOperator, TimeDependentSum, set_time!,
4040
current_time, time_shift, time_stretch, time_restrict, static_operator,
4141
#superoperators
42-
KetBraBasis, ChoiBasis, PauliBasis,
42+
KetBraBasis, ChoiBasis, PauliBasis, ChiBasis,
4343
vec, unvec, super, choi, kraus, stinespring, pauli, chi,
4444
spre, spost, sprepost, liouvillian, identitysuperoperator,
4545
SuperOperatorType, DenseSuperOpType, SparseSuperOpType,

src/pauli.jl

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11

22
# comp stands for computational basis
3-
function _comp_pauli_1(basis_fn)
3+
function _pauli_comp_1(b_out_fn, b_in)
44
b = SpinBasis(1//2)
5-
vec_it(fn) = vec(fn(b).data)/2 # provides "standard" normalization
5+
vec_it(fn) = vec((fn(b)/sqrt(2)).data) # provides "standard" normalization
66
V = hcat(map(vec_it, [identityoperator, sigmax, sigmay, sigmaz])...)
7-
Operator(basis_fn(b, b), PauliBasis(1), V)
7+
Operator(b_out_fn(b, b), b_in, V)
88
end
99

10-
_comp_pauli_kb1_cached = _comp_pauli_1(KetBraBasis)
11-
_comp_pauli_choi1_cached = _comp_pauli_1(ChoiBasis)
10+
_pauli_comp_kb1_cached = _pauli_comp_1(KetBraBasis, PauliBasis(1))
11+
_pauli_comp_choi1_cached = _pauli_comp_1(ChoiBasis, ChiBasis(2))
1212

1313
# TODO: should this be further cached?
1414
"""
@@ -17,52 +17,43 @@ _comp_pauli_choi1_cached = _comp_pauli_1(ChoiBasis)
1717
Creates a superoperator which changes from the computational `KetBra(SpinBasis(1//2))`
1818
to the `PauliBasis()` over `N` qubits.
1919
"""
20-
comp_pauli_kb(N::Integer) = tensor_pow(_comp_pauli_kb1_cached, N)
21-
comp_pauli_choi(N::Integer) = tensor_pow(_comp_pauli_choi1_cached, N)
22-
23-
function _check_is_spinbasis(b)
24-
for i=1:length(b)
25-
(b[i] isa SpinBasis && dimension(b[i]) == 2) || throw(ArgumentError("Superoperator must be over systems composed of SpinBasis(1//2) to be converted to pauli representation"))
26-
end
27-
end
20+
pauli_comp_kb(N::Integer) = tensor_pow(_pauli_comp_kb1_cached, N)
21+
pauli_comp_choi(N::Integer) = tensor_pow(_pauli_comp_choi1_cached, N)
2822

2923
# It's possible to get better asympotic speedups using, e.g. methods from
3024
# https://iopscience.iop.org/article/10.1088/1402-4896/ad6499
3125
# https://arxiv.org/abs/2411.00526
3226
# https://quantum-journal.org/papers/q-2024-09-05-1461/ (see appendices)
33-
function pauli(op::Operator)
34-
(basis_l(op) == basis_r(op)) || throw(ArgumentError("Operator must be square in order to be vectorized to the pauli represenation"))
35-
_check_is_spinbasis(basis_l(op))
36-
dagger(comp_pauli_kb(length(basis_l(op)))) * vec(op)
37-
end
27+
pauli(op::Operator) = dagger(pauli_comp_kb(length(basis(op)))) * vec(op)
3828

3929
function pauli(op::SOpKetBraType)
40-
bl, br = basis_l(op), basis_r(op)
41-
((basis_l(bl) == basis_r(bl)) && (basis_l(br) == basis_r(br))) || throw(ArgumentError("Superoperator must map between square operators in order to be converted to pauli represenation"))
42-
bl, br = basis_l(bl), basis_l(br)
43-
foreach(_check_is_spinbasis, (bl, br))
44-
45-
Nl, Nr = length(bl), length(br)
46-
Vl = comp_pauli_kb(Nl)
47-
Vr = Nl == Nr ? Vl : comp_pauli_kb(Nr)
48-
#data = dagger(Ul)*op.data*Ur # TODO figure out normalization
49-
#@assert isapprox(imag.(data), zero(data), atol=tol)
50-
#Operator(PauliBasis()^Nl, PauliBasis()^Nr, real.(data))
51-
dagger(Vl) * op * Vr # TODO figure out normalization
52-
# TODO make sure dagger here is okay...
30+
Nl, Nr = length(basis_l(basis_l(op))), length(basis_l(basis_r(op)))
31+
Vl = pauli_comp_kb(Nl)
32+
Vr = Nl == Nr ? Vl : pauli_comp_kb(Nr)
33+
dagger(Vl) * op * Vr
5334
end
5435

5536
function chi(op::ChoiStateType)
56-
(basis_l(op) == basis_r(op)) || throw(ArgumentError("Choi state must map between square operators in order to be converted to chi represenation"))
57-
bl, br = basis_l(basis_l(op)), basis_r(basis_l(op))
58-
foreach(_check_is_spinbasis, (bl, br))
37+
Nl, Nr = length(basis_l(basis_l(op))), length(basis_r(basis_l(op)))
38+
Vl = pauli_comp_choi(Nl)
39+
Vr = Nl == Nr ? Vl : pauli_comp_choi(Nr)
40+
dagger(Vl) * op * Vr
41+
end
5942

43+
function _pauli_chi(basis_fn, op)
44+
bl, br = basis_l(op), basis_r(op)
6045
Nl, Nr = length(bl), length(br)
61-
Vl = comp_pauli_kb(Nl)
62-
Vr = Nl == Nr ? Vl : comp_pauli_kb(Nr)
63-
dagger(Vl) * op * Vr # TODO figure out normalization
46+
data = reshape(op.data, (2^Nl, 2^Nl, 2^Nr, 2^Nr))
47+
data = PermutedDimsArray(data, (4, 2, 3, 1))
48+
data = reshape(data, (2^(Nl+Nr), 2^(Nl+Nr)))
49+
return Operator(basis_fn(Nl+Nr), basis_fn(Nl+Nr), data)
6450
end
6551

52+
pauli(op::ChiType) = _pauli_chi(PauliBasis, op)
53+
chi(op::SOpPauliType) = _pauli_chi(ChiBasis, op)
54+
pauli(op::ChoiStateType) = pauli(chi(op))
55+
chi(op::SOpKetBraType) = chi(pauli(op))
56+
6657
"""
6758
function hwpauli(op::SuperOperatorType; tol=1e-9)
6859
bl, br = basis_l(op), basis_r(op)

src/superoperators.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import QuantumInterface: KetBraBasis, ChoiBasis, PauliBasis
1+
import QuantumInterface: KetBraBasis, ChoiBasis, PauliBasis, ChiBasis
22
using TensorCast
33

44
const SOpBasis = Union{KetBraBasis, PauliBasis}
@@ -83,7 +83,7 @@ function _super_choi(basis_fn, op)
8383
end
8484

8585
choi(op::SuperOperatorType) = _super_choi(ChoiBasis, op)
86-
super(op::ChoiStateType) = _super_choi(KetBraBasis, op)
86+
super(op::ChoiStateType) = _super_choi(KetBraBasis, op)
8787

8888
# I'm not sure this is actually right... see sec V.C. of https://arxiv.org/abs/1111.6950
8989
dagger(a::ChoiStateType) = choi(dagger(super(a)))

test/test_pauli.jl

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,68 @@ using LinearAlgebra
22
using Test
33

44
using QuantumOpticsBase
5-
import QuantumOpticsBase: comp_pauli_kb
65

76
@testset "pauli" begin
87

9-
b = SpinBasis(1//2)
10-
Isop = sprepost(identityoperator(b), dagger(identityoperator(b)))
11-
Xsop = sprepost(sigmax(b), dagger(sigmax(b)))
12-
Ysop = sprepost(sigmay(b), dagger(sigmay(b)))
13-
Zsop = sprepost(sigmaz(b), dagger(sigmaz(b)))
14-
Xsk = vec(sigmax(b))
15-
V = comp_pauli_kb(1)
16-
8+
bs = SpinBasis(1//2)
9+
bp = PauliBasis(1)
10+
11+
# Test Pauli basis vectors are in I, X, Y, Z order
12+
@test pauli(identityoperator(bs)/sqrt(2)).data [1., 0, 0, 0]
13+
@test pauli(sigmax(bs)/sqrt(2)).data [0, 1., 0, 0]
14+
@test pauli(sigmay(bs)/sqrt(2)).data [0, 0, 1., 0]
15+
@test pauli(sigmaz(bs)/sqrt(2)).data [0, 0, 0, 1.]
16+
17+
# Test that single qubit unitary Pauli channels are diagonal
18+
Isop = sprepost(identityoperator(bs), dagger(identityoperator(bs)))
19+
Xsop = sprepost(sigmax(bs), dagger(sigmax(bs)))
20+
Ysop = sprepost(sigmay(bs), dagger(sigmay(bs)))
21+
Zsop = sprepost(sigmaz(bs), dagger(sigmaz(bs)))
22+
@test pauli(Isop).data diagm([1, 1, 1, 1])
23+
@test pauli(Xsop).data diagm([1, 1, -1, -1])
24+
@test pauli(Ysop).data diagm([1, -1, 1, -1])
25+
@test pauli(Zsop).data diagm([1, -1, -1, 1])
26+
27+
# Test bit flip encoder isometry
28+
encoder_kraus = (tensor_pow(spinup(bs), 3) dagger(spinup(bs)) +
29+
tensor_pow(spindown(bs), 3) dagger(spindown(bs)))
30+
encoder_sup = sprepost(encoder_kraus, dagger(encoder_kraus))
31+
decoder_sup = sprepost(dagger(encoder_kraus), encoder_kraus)
32+
@test super(choi(encoder_sup)).data == encoder_sup.data
33+
@test decoder_sup == dagger(encoder_sup)
34+
@test choi(decoder_sup) == dagger(choi(encoder_sup))
35+
@test decoder_sup*encoder_sup dense(identitysuperoperator(bs))
36+
@test decoder_sup*choi(encoder_sup) dense(identitysuperoperator(bs))
37+
@test choi(decoder_sup)*encoder_sup dense(identitysuperoperator(bs))
38+
@test super(choi(decoder_sup)*choi(encoder_sup)) dense(identitysuperoperator(bs))
1739

1840
# Test conversion of unitary matrices to superoperators.
19-
CZ = dm(spinup(b))identityoperator(b) + dm(spindown(b))sigmaz(b)
41+
CZ = dm(spinup(bs))identityoperator(bs) + dm(spindown(bs))sigmaz(bs)
2042
CZ_sop = sprepost(CZ,dagger(CZ))
2143

22-
# Test conversion of unitary matrices to superoperators.
23-
@test diag(CZ_sop.data) == ComplexF64[1,1,1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1]
24-
@test basis_l(CZ_sop) == basis_r(CZ_sop) == KetBraBasis(b^2, b^2)
44+
@test CZ_sop.data diagm([1,1,1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1])
45+
@test basis_l(CZ_sop) == basis_r(CZ_sop) == KetBraBasis(bs^2, bs^2)
2546

2647
# Test conversion of superoperator to Pauli transfer matrix.
2748
CZ_ptm = pauli(CZ_sop)
2849

29-
# Test DensePauliTransferMatrix constructor.
50+
# Test construction of dense Pauli transfer matrix
3051
@test_throws DimensionMismatch Operator(PauliBasis(2), PauliBasis(3), CZ_ptm.data)
3152
@test Operator(PauliBasis(2), PauliBasis(2), CZ_ptm.data) == CZ_ptm
3253

33-
@test all(isapprox.(CZ_ptm.data[[1,30,47,52,72,91,117,140,166,185,205,210,227,256]], 4))
34-
@test all(isapprox.(CZ_ptm.data[[106,151]], -4))
54+
@test all(isapprox.(CZ_ptm.data[[1,30,47,52,72,91,117,140,166,185,205,210,227,256]], 1))
55+
@test all(isapprox.(CZ_ptm.data[[106,151]], -1))
3556

36-
@test CZ_ptm == PauliTransferMatrix(ChiMatrix(CZ))
57+
@test CZ_ptm == pauli(chi(CZ_sop))
3758

38-
# Test construction of non-symmetric unitary.
39-
CNOT = DenseOperator(b^2, b^2, diagm(0 => [1,1,0,0], 1 => [0,0,1], -1 => [0,0,1]))
40-
CNOT_sop = SuperOperator(CNOT)
41-
CNOT_chi = ChiMatrix(CNOT)
42-
CNOT_ptm = PauliTransferMatrix(CNOT)
59+
CNOT = dm(spinup(bs))identityoperator(bs) + dm(spindown(bs))sigmax(bs)
60+
CNOT_sop = sprepost(CZ,dagger(CZ))
61+
CNOT_chi = chi(CNOT_sop)
62+
CNOT_ptm = pauli(CNOT_sop)
4363

44-
@test CNOT_sop.basis_l == CNOT_sop.basis_r == (b^2, b^2)
45-
@test CNOT_chi.basis_l == CNOT_chi.basis_r == (b^2, b^2)
46-
@test CNOT_ptm.basis_l == CNOT_ptm.basis_r == (b^2, b^2)
64+
@test basis_l(CNOT_sop) == basis_r(CNOT_sop) == KetBraBasis(bs^2, bs^2)
65+
@test basis_l(CNOT_chi) == basis_r(CNOT_chi) == ChoiBasis(bp^2, bp^2)
66+
@test basis_l(CNOT_ptm) == basis_r(CNOT_ptm) == ChoiBasis(bp^2, bp^2)
4767

4868
@test all(isapprox.(imag.(CNOT_sop.data), 0))
4969
@test all(isapprox.(imag.(CNOT_chi.data), 0))
@@ -55,25 +75,25 @@ CNOT_ptm = PauliTransferMatrix(CNOT)
5575
@test all(isapprox.(CNOT_ptm.data[[1,18,47,64,70,85,108,138,153,183,205,222,227,244,]], 1))
5676
@test all(isapprox.(CNOT_ptm.data[[123,168]], -1))
5777

58-
# Test DenseChiMatrix constructor.
59-
@test_throws DimensionMismatch Operator(ChoiBasis(PauliBasis(2), PauliBasis(2)), ChoiBasis(PauliBasis(3), PauliBasis(3)), CNOT_chi.data)
60-
@test Operator(ChoiBasis(PauliBasis(2), PauliBasis(2)), CNOT_chi.data) == CNOT_chi
78+
# Test construction of chi matrix
79+
@test_throws DimensionMismatch Operator(ChoiBasis(bp^2, bp^2), ChoiBasis(bp^3, bp^3), CNOT_chi.data)
80+
@test Operator(ChoiBasis(bp^2, bp^2), CNOT_chi.data) == CNOT_chi
6181

62-
# Test equality and conversion among all three bases.
82+
# Test equality and conversion of identity among all three bases.
6383
ident = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]
6484

65-
IDENT = DenseOperator(b^2, ident)
85+
IDENT = DenseOperator(bs^2, ident)
6686

6787
IDENT_sop = SuperOperator(IDENT)
6888
IDENT_chi = ChiMatrix(IDENT)
6989
IDENT_ptm = PauliTransferMatrix(IDENT)
7090

71-
@test ChiMatrix(IDENT_sop) == IDENT_chi
72-
@test ChiMatrix(IDENT_ptm) == IDENT_chi
73-
@test SuperOperator(IDENT_chi) == IDENT_sop
74-
@test SuperOperator(IDENT_ptm) == IDENT_sop
75-
@test PauliTransferMatrix(IDENT_sop) == IDENT_ptm
76-
@test PauliTransferMatrix(IDENT_chi) == IDENT_ptm
91+
@test chi(IDENT_sop) == IDENT_chi
92+
@test chi(IDENT_ptm) == IDENT_chi
93+
@test super(IDENT_chi) == IDENT_sop
94+
@test super(IDENT_ptm) == IDENT_sop
95+
@test pauli(IDENT_sop) == IDENT_ptm
96+
@test pauli(IDENT_chi) == IDENT_ptm
7797

7898
# Test approximate equality and conversion among all three bases.
7999
cphase = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 exp(1im*.6)]

test/test_superoperators.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ b_fock = FockBasis(5)
224224
z_l = normalize(fockstate(b_fock, 0) + fockstate(b_fock, 4))
225225
o_l = fockstate(b_fock, 2)
226226
encoder_kraus = z_l dagger(spinup(b_logical)) + o_l dagger(spindown(b_logical))
227-
encoder_sup = sprepost(encoder_kraus, dagger(encoder_kraus))
227+
encoder_sup = sprepost(encoder_kraus, dagger(encoder_kraus))
228228
decoder_sup = sprepost(dagger(encoder_kraus), encoder_kraus)
229229
@test super(choi(encoder_sup)).data == encoder_sup.data
230230
@test decoder_sup == dagger(encoder_sup)

0 commit comments

Comments
 (0)