From e5b0947b0e67556d7524c8fb6c51a0522373ac82 Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Tue, 14 May 2019 08:10:44 -0700 Subject: [PATCH 1/7] created abstractions for quantum information on qubits --- src/QuantumOptics.jl | 4 +- src/pauli.jl | 217 ++++++++++++++++++++++++++++++++++++ src/spin.jl | 12 ++ src/superoperators.jl | 1 - test/test_pauli.jl | 26 +++++ test/test_superoperators.jl | 6 + 6 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 src/pauli.jl create mode 100644 test/test_pauli.jl diff --git a/src/QuantumOptics.jl b/src/QuantumOptics.jl index 09ec19e4..dca23194 100644 --- a/src/QuantumOptics.jl +++ b/src/QuantumOptics.jl @@ -35,7 +35,7 @@ export bases, Basis, GenericBasis, CompositeBasis, basis, steadystate, timecorrelations, semiclassical, - stochastic + stochastic, PauliBasis, PauliTransferMatrix, ChiMatrix include("sortedindices.jl") @@ -86,6 +86,7 @@ module stochastic using .stochastic_definitions end include("printing.jl") +include("pauli.jl") using .bases using .states @@ -109,6 +110,7 @@ using .metrics using .spectralanalysis using .timecorrelations using .printing +using .pauli end # module diff --git a/src/pauli.jl b/src/pauli.jl new file mode 100644 index 00000000..81e3e8e4 --- /dev/null +++ b/src/pauli.jl @@ -0,0 +1,217 @@ +module pauli + +export PauliBasis, PauliTransferMatrix, ChiMatrix + +import Base: == + +using ..bases, ..spin, ..superoperators +using ..operators: identityoperator, AbstractOperator +using ..superoperators: SuperOperator +using ..operators_dense: DenseOperator +using ..spin: sigmax, sigmay, sigmaz +using SparseArrays +using LinearAlgebra: tr + +""" + PauliBasis(num_qubits::Int) + +Basis for an N-qubit space where `num_qubits` specifices the number of qubits. +The dimension of the basis is 2²ᴺ. +""" +mutable struct PauliBasis{B<:Tuple{Vararg{Basis}}} <: Basis + shape::Vector{Int} + bases::B + function PauliBasis(num_qubits::Int64) + return new{Tuple{(SpinBasis{1//2} for _ in 1:num_qubits)...}}([2 for _ in 1:num_qubits], Tuple(SpinBasis(1//2) for _ in 1:num_qubits)) + end +end +==(pb1::PauliBasis, pb2::PauliBasis) = length(pb1.bases) == length(pb2.bases) + +""" +Base class for Pauli transfer matrix classes. +""" +abstract type PauliTransferMatrix{B1<:Tuple{PauliBasis, PauliBasis}, B2<:Tuple{PauliBasis, PauliBasis}} end + + +""" + DensePauliTransferMatrix(b, b, data) + +DensePauliTransferMatrix stored as a dense matrix. +""" +mutable struct DensePauliTransferMatrix{B1<:Tuple{PauliBasis, PauliBasis}, + B2<:Tuple{PauliBasis, PauliBasis}, + T<:Matrix{Float64}} <: PauliTransferMatrix{B1, B2} + basis_l::B1 + basis_r::B2 + data::T + function DensePauliTransferMatrix(basis_l::BL, basis_r::BR, data::T) where {BL<:Tuple{PauliBasis, PauliBasis}, + BR<:Tuple{PauliBasis, PauliBasis}, + T<:Matrix{Float64}} + if length(basis_l[1])*length(basis_l[2]) != size(data, 1) || + length(basis_r[1])*length(basis_r[2]) != size(data, 2) + throw(DimensionMismatch()) + end + new{BL, BR, T}(basis_l, basis_r, data) + end +end + +""" +Base class for χ (process) matrix classes. +""" +abstract type ChiMatrix{B1<:Tuple{PauliBasis, PauliBasis}, B2<:Tuple{PauliBasis, PauliBasis}} end + +""" + DenseChiMatrix(b, b, data) + +DenseChiMatrix stored as a dense matrix. +""" +mutable struct DenseChiMatrix{B1<:Tuple{PauliBasis, PauliBasis}, + B2<:Tuple{PauliBasis, PauliBasis}, + T<:Matrix{Complex{Float64}}} <: PauliTransferMatrix{B1, B2} + basis_l::B1 + basis_r::B2 + data::T + function DenseChiMatrix(basis_l::BL, basis_r::BR, data::T) where {BL<:Tuple{PauliBasis, PauliBasis}, + BR<:Tuple{PauliBasis, PauliBasis}, + T<:Matrix{Complex{Float64}}} + if length(basis_l[1])*length(basis_l[2]) != size(data, 1) || + length(basis_r[1])*length(basis_r[2]) != size(data, 2) + throw(DimensionMismatch()) + end + new{BL, BR, T}(basis_l, basis_r, data) + end +end + +""" + pauli_operators(num_qubits::Int64) + +Generate a matrix of basis vectors in the Pauli representation given a number +of qubits. +""" +function pauli_operators(num_qubits::Int64) + pauli_funcs = (identityoperator, sigmax, sigmay, sigmaz) + po = [] + for paulis in Iterators.product((pauli_funcs for _ in 1:num_qubits)...) + basis_vector = reduce(⊗, f(SpinBasis(1//2)) for f in paulis) + push!(po, basis_vector) + end + return po +end + +""" + pauli_basis_vectors(num_qubits::Int64) + +Generate a matrix of basis vectors in the Pauli representation given a number +of qubits. +""" +function pauli_basis_vectors(num_qubits::Int64) + po = pauli_operators(num_qubits) + sop_dim = length(po) + return mapreduce(x -> sparse(reshape(x.data, sop_dim)), (x, y) -> [x y], po) +end + +""" + PauliTransferMatrix(sop::DenseSuperOperator) + +Convert a superoperator to its representation as a Pauli transfer matrix. +""" +function PauliTransferMatrix(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} + num_qubits = length(sop.basis_l[1].bases) + pbv = pauli_basis_vectors(num_qubits) + sop_dim = 2 ^ (2 * num_qubits) + data = Array{Float64}(undef, (sop_dim, sop_dim)) + data .= pbv' * sop.data * pbv / √sop_dim + return DensePauliTransferMatrix(sop.basis_l, sop.basis_r, data) +end + +function SuperOperator(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis + return spre(unitary) * spost(unitary') +end + +""" + SuperOperator(ptm::DensePauliTransferMatrix) + +Convert a Pauli transfer matrix to its representation as a superoperator. +""" +function SuperOperator(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where B <: Tuple{PauliBasis, PauliBasis} + num_qubits = length(ptm.basis_l[1].bases) + pbv = pauli_basis_vectors(num_qubits) + sop_dim = 2 ^ (2 * num_qubits) + data = Array{Complex{Float64}}(undef, (sop_dim, sop_dim)) + data .= pbv * ptm.data * pbv' / √sop_dim + return DenseSuperOperator(ptm.basis_l, ptm.basis_r, data) +end + +""" + PauliTransferMatrix(unitary::DenseOperator) + +Convert an operator, presumably a unitary operator, to its representation as a +Pauli transfer matrix. +""" +PauliTransferMatrix(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis = PauliTransferMatrix(SuperOperator(unitary)) + +""" + ChiMatrix(unitary::DenseOperator) + +Convert an operator, presumably a unitary operator, to its representation as a χ matrix. +""" +function ChiMatrix(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis + num_qubits = length(unitary.basis_l.bases) + pbv = pauli_basis_vectors(num_qubits) + aj = pbv' * reshape(unitary.data, 2 ^ (2 * num_qubits)) + return DenseChiMatrix((unitary.basis_l, unitary.basis_l), (unitary.basis_r, unitary.basis_r), aj * aj' / (2 ^ num_qubits)) +end + +""" + ChiMatrix(sop::DenseSuperOperator) + +Convert a superoperator to its representation as a Chi matrix. +""" +function ChiMatrix(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} + num_qubits = length(sop.basis_l) + sop_dim = 2 ^ (2 * num_qubits) + po = pauli_operators(num_qubits) + data = Array{Complex{Float64}, 2}(undef, (sop_dim, sop_dim)) + for (idx, jdx) in Iterators.product(1:sop_dim, 1:sop_dim) + data[idx, jdx] = tr((spre(po[idx]) * spost(po[jdx])).data' .* sop.data) / √sop_dim + end + return DenseChiMatrix(sop.basis_l, sop.basis_r, data) +end + +""" + PauliTransferMatrix(chi_matrix::DenseChiMatrix) + +Convert a χ matrix to its representation as a Pauli transfer matrix. +""" +function PauliTransferMatrix(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} + num_qubits = length(chi_matrix.basis_l) + sop_dim = 2 ^ (2 * num_qubits) + po = pauli_operators(num_qubits) + data = Array{Float64, 2}(undef, (sop_dim, sop_dim)) + for (idx, jdx) in Iterators.product(1:sop_dim, 1:sop_dim) + data[idx, jdx] = tr(mapreduce(x -> po[idx] * po[x[1]] * po[jdx] * po[x[2]] * chi_matrix.data[x[1], x[2]], + +, + Iterators.product(1:16, 1:16)).data) / sop_dim + end + return DensePauliTransferMatrix(chi_matrix.basis_l, chi_matrix.basis_r, data) +end + +""" + SuperOperator(chi_matrix::DenseChiMatrix) + +Convert a χ matrix to its representation as a superoperator. +""" +function SuperOperator(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} + return SuperOperator(PauliTransferMatrix(chi_matrix)) +end + +""" + ChiMatrix(ptm::PauliTransferMatrix) + +Convert a Pauli transfer matrix to its representation as a χ matrix. +""" +function ChiMatrix(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where B <: Tuple{PauliBasis, PauliBasis} + return ChiMatrix(SuperOperator(ptm)) +end + +end # end module diff --git a/src/spin.jl b/src/spin.jl index f6fa1e31..b2b662bf 100644 --- a/src/spin.jl +++ b/src/spin.jl @@ -34,6 +34,18 @@ SpinBasis(spinnumber::Int) = SpinBasis(convert(Rational{Int}, spinnumber)) ==(b1::SpinBasis, b2::SpinBasis) = b1.spinnumber==b2.spinnumber +""" + identityoperator(b::SpinBasis) + +Pauli ``I`` operator for the given Spin basis. +""" +function identityoperator(b::SpinBasis) + N = length(b) + diag = ComplexF64[complex(1) for m=b.spinnumber:-1:-b.spinnumber] + data = spdiagm(0 => diag) + SparseOperator(b, data) +end + """ sigmax(b::SpinBasis) diff --git a/src/superoperators.jl b/src/superoperators.jl index b5ca853a..908325ab 100644 --- a/src/superoperators.jl +++ b/src/superoperators.jl @@ -11,7 +11,6 @@ import SparseArrays: sparse using ..bases, ..operators, ..operators_dense, ..operators_sparse using SparseArrays - """ Base class for all super operator classes. diff --git a/test/test_pauli.jl b/test/test_pauli.jl new file mode 100644 index 00000000..b7729260 --- /dev/null +++ b/test/test_pauli.jl @@ -0,0 +1,26 @@ +using LinearAlgebra +using Test + +using QuantumOptics + +@testset "pauli" begin + +@test_throws MethodError PauliBasis(1.4) + +# Test conversion of unitary matrices to superoperators. +q2 = PauliBasis(2) +CZ = DenseOperator(q2, q2, diagm(0 => [1,1,1,-1])) +sopCZ = SuperOperator(CZ) + +# Test conversion of superoperator to Pauli transfer matrix. +ptmCZ = PauliTransferMatrix(sopCZ) +cz = zeros(Float64, (16, 16)) +for idx in [1,30,47,52,72,91,117,140,166,185,205,210,227,256] + cz[idx] = 1 +end +for idx in [106,151] + cz[idx] = -1 +end +@test ptmCZ.data == cz + +end # testset diff --git a/test/test_superoperators.jl b/test/test_superoperators.jl index 543e25d4..98d2a9bf 100644 --- a/test/test_superoperators.jl +++ b/test/test_superoperators.jl @@ -115,6 +115,12 @@ x = -s1 @test x.basis_l == (b1, b2) @test x.basis_r == (b3, b4) +# Test conversion of unitary matrices to superoperators. +b5 = GenericBasis(4) +CZ = DenseOperator(b5, b5, diagm(0 => [1,1,1,-1])) +sopCZ = SuperOperator(CZ) +@test diag(sopCZ.data) == ComplexF64[1,1,1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1] +@test sopCZ.basis_l == sopCZ.basis_r == (b5, b5) # TODO: Clean-up this part ωc = 1.2 From ce25ba75d3e244efd527f0d3f3d721b4532f85b3 Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Wed, 5 Jun 2019 10:55:33 -0700 Subject: [PATCH 2/7] changed how equality is handled and added tests --- src/pauli.jl | 24 ++++++++++++++++------ src/superoperators.jl | 1 + test/runtests.jl | 1 + test/test_pauli.jl | 41 ++++++++++++++++++++++++++++--------- test/test_superoperators.jl | 6 ------ 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/pauli.jl b/src/pauli.jl index 81e3e8e4..57fb1ade 100644 --- a/src/pauli.jl +++ b/src/pauli.jl @@ -55,6 +55,8 @@ mutable struct DensePauliTransferMatrix{B1<:Tuple{PauliBasis, PauliBasis}, end end +PauliTransferMatrix(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = ptm + """ Base class for χ (process) matrix classes. """ @@ -82,6 +84,8 @@ mutable struct DenseChiMatrix{B1<:Tuple{PauliBasis, PauliBasis}, end end +DenseChiMatrix(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = chi_matrix + """ pauli_operators(num_qubits::Int64) @@ -120,13 +124,12 @@ function PauliTransferMatrix(sop::DenseSuperOperator{B, B, Array{Complex{Float64 pbv = pauli_basis_vectors(num_qubits) sop_dim = 2 ^ (2 * num_qubits) data = Array{Float64}(undef, (sop_dim, sop_dim)) - data .= pbv' * sop.data * pbv / √sop_dim + data .= real.(pbv' * sop.data * pbv / √sop_dim) return DensePauliTransferMatrix(sop.basis_l, sop.basis_r, data) end -function SuperOperator(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis - return spre(unitary) * spost(unitary') -end +SuperOperator(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis = spre(unitary) * spost(unitary') +SuperOperator(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = sop """ SuperOperator(ptm::DensePauliTransferMatrix) @@ -173,7 +176,7 @@ function ChiMatrix(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) wh po = pauli_operators(num_qubits) data = Array{Complex{Float64}, 2}(undef, (sop_dim, sop_dim)) for (idx, jdx) in Iterators.product(1:sop_dim, 1:sop_dim) - data[idx, jdx] = tr((spre(po[idx]) * spost(po[jdx])).data' .* sop.data) / √sop_dim + data[idx, jdx] = tr((spre(po[idx]) * spost(po[jdx])).data' * sop.data) / √sop_dim end return DenseChiMatrix(sop.basis_l, sop.basis_r, data) end @@ -191,7 +194,7 @@ function PauliTransferMatrix(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Floa for (idx, jdx) in Iterators.product(1:sop_dim, 1:sop_dim) data[idx, jdx] = tr(mapreduce(x -> po[idx] * po[x[1]] * po[jdx] * po[x[2]] * chi_matrix.data[x[1], x[2]], +, - Iterators.product(1:16, 1:16)).data) / sop_dim + Iterators.product(1:16, 1:16)).data) / sop_dim |> real end return DensePauliTransferMatrix(chi_matrix.basis_l, chi_matrix.basis_r, data) end @@ -214,4 +217,13 @@ function ChiMatrix(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where return ChiMatrix(SuperOperator(ptm)) end +""" +Equality for all varieties of superoperators. +""" +==(sop1::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}, + sop2::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}) = ((typeof(sop1) == typeof(sop2)) & + (sop1.basis_l == sop2.basis_l) & + (sop1.basis_r == sop2.basis_r) & + isapprox(sop1.data, sop2.data)) + end # end module diff --git a/src/superoperators.jl b/src/superoperators.jl index 908325ab..b5ca853a 100644 --- a/src/superoperators.jl +++ b/src/superoperators.jl @@ -11,6 +11,7 @@ import SparseArrays: sparse using ..bases, ..operators, ..operators_dense, ..operators_sparse using SparseArrays + """ Base class for all super operator classes. diff --git a/test/runtests.jl b/test/runtests.jl index 08db82cd..ce75d3f9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -46,6 +46,7 @@ names = [ "test_stochastic_schroedinger.jl", "test_stochastic_master.jl", "test_stochastic_semiclassical.jl", + "test_pauli.jl", "test_printing.jl" ] diff --git a/test/test_pauli.jl b/test/test_pauli.jl index b7729260..fc1ee555 100644 --- a/test/test_pauli.jl +++ b/test/test_pauli.jl @@ -10,17 +10,38 @@ using QuantumOptics # Test conversion of unitary matrices to superoperators. q2 = PauliBasis(2) CZ = DenseOperator(q2, q2, diagm(0 => [1,1,1,-1])) -sopCZ = SuperOperator(CZ) +CZ_sop = SuperOperator(CZ) + +# Test conversion of unitary matrices to superoperators. +@test diag(CZ_sop.data) == ComplexF64[1,1,1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1] +@test CZ_sop.basis_l == CZ_sop.basis_r == (q2, q2) # Test conversion of superoperator to Pauli transfer matrix. -ptmCZ = PauliTransferMatrix(sopCZ) -cz = zeros(Float64, (16, 16)) -for idx in [1,30,47,52,72,91,117,140,166,185,205,210,227,256] - cz[idx] = 1 -end -for idx in [106,151] - cz[idx] = -1 -end -@test ptmCZ.data == cz +CZ_ptm = PauliTransferMatrix(CZ_sop) + +CZ_ptm_test = zeros(Float64, (16, 16)) +CZ_ptm_test[[1,30,47,52,72,91,117,140,166,185,205,210,227,256]] = ones(14) +CZ_ptm_test[[106,151]] = -1 * ones(2) + +@test CZ_ptm.data == CZ_ptm_test +@test CZ_ptm == PauliTransferMatrix(ChiMatrix(CZ)) + +# Test conversion among all three bases. +cphase = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 exp(1im*.6)] + +q2 = PauliBasis(2) + +CPHASE = DenseOperator(q2, cphase) + +CPHASE_sop = SuperOperator(CPHASE) +CPHASE_chi = ChiMatrix(CPHASE) +CPHASE_ptm = PauliTransferMatrix(CPHASE) + +@test ChiMatrix(CPHASE_sop) == CPHASE_chi +@test ChiMatrix(CPHASE_ptm) == CPHASE_chi +@test SuperOperator(CPHASE_chi) == CPHASE_sop +@test SuperOperator(CPHASE_ptm) == CPHASE_sop +@test PauliTransferMatrix(CPHASE_sop) == CPHASE_ptm +@test PauliTransferMatrix(CPHASE_chi) == CPHASE_ptm end # testset diff --git a/test/test_superoperators.jl b/test/test_superoperators.jl index 98d2a9bf..543e25d4 100644 --- a/test/test_superoperators.jl +++ b/test/test_superoperators.jl @@ -115,12 +115,6 @@ x = -s1 @test x.basis_l == (b1, b2) @test x.basis_r == (b3, b4) -# Test conversion of unitary matrices to superoperators. -b5 = GenericBasis(4) -CZ = DenseOperator(b5, b5, diagm(0 => [1,1,1,-1])) -sopCZ = SuperOperator(CZ) -@test diag(sopCZ.data) == ComplexF64[1,1,1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1] -@test sopCZ.basis_l == sopCZ.basis_r == (b5, b5) # TODO: Clean-up this part ωc = 1.2 From f68f832c9ac3134fdbaad0839567396b9023740f Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Wed, 26 Jun 2019 09:58:57 -0700 Subject: [PATCH 3/7] code review --- src/pauli.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pauli.jl b/src/pauli.jl index 57fb1ade..ccaa80b6 100644 --- a/src/pauli.jl +++ b/src/pauli.jl @@ -15,13 +15,13 @@ using LinearAlgebra: tr """ PauliBasis(num_qubits::Int) -Basis for an N-qubit space where `num_qubits` specifices the number of qubits. +Basis for an N-qubit space where `num_qubits` specifies the number of qubits. The dimension of the basis is 2²ᴺ. """ mutable struct PauliBasis{B<:Tuple{Vararg{Basis}}} <: Basis shape::Vector{Int} bases::B - function PauliBasis(num_qubits::Int64) + function PauliBasis(num_qubits::Int) return new{Tuple{(SpinBasis{1//2} for _ in 1:num_qubits)...}}([2 for _ in 1:num_qubits], Tuple(SpinBasis(1//2) for _ in 1:num_qubits)) end end @@ -34,7 +34,7 @@ abstract type PauliTransferMatrix{B1<:Tuple{PauliBasis, PauliBasis}, B2<:Tuple{P """ - DensePauliTransferMatrix(b, b, data) + DensePauliTransferMatrix(B1, B2, data) DensePauliTransferMatrix stored as a dense matrix. """ @@ -87,12 +87,12 @@ end DenseChiMatrix(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = chi_matrix """ - pauli_operators(num_qubits::Int64) + pauli_operators(num_qubits::Int) Generate a matrix of basis vectors in the Pauli representation given a number of qubits. """ -function pauli_operators(num_qubits::Int64) +function pauli_operators(num_qubits::Int) pauli_funcs = (identityoperator, sigmax, sigmay, sigmaz) po = [] for paulis in Iterators.product((pauli_funcs for _ in 1:num_qubits)...) @@ -103,12 +103,12 @@ function pauli_operators(num_qubits::Int64) end """ - pauli_basis_vectors(num_qubits::Int64) + pauli_basis_vectors(num_qubits::Int) Generate a matrix of basis vectors in the Pauli representation given a number of qubits. """ -function pauli_basis_vectors(num_qubits::Int64) +function pauli_basis_vectors(num_qubits::Int) po = pauli_operators(num_qubits) sop_dim = length(po) return mapreduce(x -> sparse(reshape(x.data, sop_dim)), (x, y) -> [x y], po) @@ -209,7 +209,7 @@ function SuperOperator(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, end """ - ChiMatrix(ptm::PauliTransferMatrix) + ChiMatrix(ptm::DensePauliTransferMatrix) Convert a Pauli transfer matrix to its representation as a χ matrix. """ From 807aa09ed35d6491021607bc893c22cb0b1e19fa Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Thu, 4 Jul 2019 14:41:23 -0700 Subject: [PATCH 4/7] cleanup on types and added tests --- src/QuantumOptics.jl | 3 +- src/pauli.jl | 70 +++++++++++++++++++++++--------------------- test/test_pauli.jl | 39 +++++++++++++++++++----- 3 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/QuantumOptics.jl b/src/QuantumOptics.jl index dca23194..a1c2c4cd 100644 --- a/src/QuantumOptics.jl +++ b/src/QuantumOptics.jl @@ -35,7 +35,8 @@ export bases, Basis, GenericBasis, CompositeBasis, basis, steadystate, timecorrelations, semiclassical, - stochastic, PauliBasis, PauliTransferMatrix, ChiMatrix + stochastic, PauliBasis, PauliTransferMatrix, DensePauliTransferMatrix, + ChiMatrix, DenseChiMatrix include("sortedindices.jl") diff --git a/src/pauli.jl b/src/pauli.jl index ccaa80b6..02567304 100644 --- a/src/pauli.jl +++ b/src/pauli.jl @@ -1,6 +1,7 @@ module pauli -export PauliBasis, PauliTransferMatrix, ChiMatrix +export PauliBasis, PauliTransferMatrix, DensePauliTransferMatrix, + ChiMatrix, DenseChiMatrix import Base: == @@ -9,7 +10,7 @@ using ..operators: identityoperator, AbstractOperator using ..superoperators: SuperOperator using ..operators_dense: DenseOperator using ..spin: sigmax, sigmay, sigmaz -using SparseArrays +using SparseArrays: sparse using LinearAlgebra: tr """ @@ -22,13 +23,16 @@ mutable struct PauliBasis{B<:Tuple{Vararg{Basis}}} <: Basis shape::Vector{Int} bases::B function PauliBasis(num_qubits::Int) - return new{Tuple{(SpinBasis{1//2} for _ in 1:num_qubits)...}}([2 for _ in 1:num_qubits], Tuple(SpinBasis(1//2) for _ in 1:num_qubits)) + type = Tuple{(SpinBasis{1//2} for _ in 1:num_qubits)...} + shape = [2 for _ in 1:num_qubits] + bases = Tuple(SpinBasis(1//2) for _ in 1:num_qubits) + return new{type}(shape, bases) end end ==(pb1::PauliBasis, pb2::PauliBasis) = length(pb1.bases) == length(pb2.bases) """ -Base class for Pauli transfer matrix classes. + Base class for Pauli transfer matrix classes. """ abstract type PauliTransferMatrix{B1<:Tuple{PauliBasis, PauliBasis}, B2<:Tuple{PauliBasis, PauliBasis}} end @@ -55,10 +59,10 @@ mutable struct DensePauliTransferMatrix{B1<:Tuple{PauliBasis, PauliBasis}, end end -PauliTransferMatrix(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = ptm +PauliTransferMatrix(ptm::DensePauliTransferMatrix{B, B, Matrix{Float64}}) where B <: Tuple{PauliBasis, PauliBasis} = ptm """ -Base class for χ (process) matrix classes. + Base class for χ (process) matrix classes. """ abstract type ChiMatrix{B1<:Tuple{PauliBasis, PauliBasis}, B2<:Tuple{PauliBasis, PauliBasis}} end @@ -69,13 +73,13 @@ DenseChiMatrix stored as a dense matrix. """ mutable struct DenseChiMatrix{B1<:Tuple{PauliBasis, PauliBasis}, B2<:Tuple{PauliBasis, PauliBasis}, - T<:Matrix{Complex{Float64}}} <: PauliTransferMatrix{B1, B2} + T<:Matrix{ComplexF64}} <: PauliTransferMatrix{B1, B2} basis_l::B1 basis_r::B2 data::T function DenseChiMatrix(basis_l::BL, basis_r::BR, data::T) where {BL<:Tuple{PauliBasis, PauliBasis}, BR<:Tuple{PauliBasis, PauliBasis}, - T<:Matrix{Complex{Float64}}} + T<:Matrix{ComplexF64}} if length(basis_l[1])*length(basis_l[2]) != size(data, 1) || length(basis_r[1])*length(basis_r[2]) != size(data, 2) throw(DimensionMismatch()) @@ -84,13 +88,13 @@ mutable struct DenseChiMatrix{B1<:Tuple{PauliBasis, PauliBasis}, end end -DenseChiMatrix(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = chi_matrix +ChiMatrix(chi_matrix::DenseChiMatrix{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} = chi_matrix +# TODO MAKE A GENERATOR FUNCTION """ pauli_operators(num_qubits::Int) -Generate a matrix of basis vectors in the Pauli representation given a number -of qubits. +Generate a list of N-qubit Pauli operators. """ function pauli_operators(num_qubits::Int) pauli_funcs = (identityoperator, sigmax, sigmay, sigmaz) @@ -110,7 +114,7 @@ of qubits. """ function pauli_basis_vectors(num_qubits::Int) po = pauli_operators(num_qubits) - sop_dim = length(po) + sop_dim = 4 ^ num_qubits return mapreduce(x -> sparse(reshape(x.data, sop_dim)), (x, y) -> [x y], po) end @@ -119,28 +123,28 @@ end Convert a superoperator to its representation as a Pauli transfer matrix. """ -function PauliTransferMatrix(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} +function PauliTransferMatrix(sop::DenseSuperOperator{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} num_qubits = length(sop.basis_l[1].bases) pbv = pauli_basis_vectors(num_qubits) - sop_dim = 2 ^ (2 * num_qubits) - data = Array{Float64}(undef, (sop_dim, sop_dim)) + sop_dim = 4 ^ num_qubits + data = Matrix{Float64}(undef, (sop_dim, sop_dim)) data .= real.(pbv' * sop.data * pbv / √sop_dim) return DensePauliTransferMatrix(sop.basis_l, sop.basis_r, data) end -SuperOperator(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis = spre(unitary) * spost(unitary') -SuperOperator(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} = sop +SuperOperator(unitary::DenseOperator{B, B, Matrix{ComplexF64}}) where B <: PauliBasis = spre(unitary) * spost(unitary') +SuperOperator(sop::DenseSuperOperator{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} = sop """ SuperOperator(ptm::DensePauliTransferMatrix) Convert a Pauli transfer matrix to its representation as a superoperator. """ -function SuperOperator(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where B <: Tuple{PauliBasis, PauliBasis} +function SuperOperator(ptm::DensePauliTransferMatrix{B, B, Matrix{Float64}}) where B <: Tuple{PauliBasis, PauliBasis} num_qubits = length(ptm.basis_l[1].bases) pbv = pauli_basis_vectors(num_qubits) - sop_dim = 2 ^ (2 * num_qubits) - data = Array{Complex{Float64}}(undef, (sop_dim, sop_dim)) + sop_dim = 4 ^ num_qubits + data = Matrix{ComplexF64}(undef, (sop_dim, sop_dim)) data .= pbv * ptm.data * pbv' / √sop_dim return DenseSuperOperator(ptm.basis_l, ptm.basis_r, data) end @@ -151,17 +155,17 @@ end Convert an operator, presumably a unitary operator, to its representation as a Pauli transfer matrix. """ -PauliTransferMatrix(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis = PauliTransferMatrix(SuperOperator(unitary)) +PauliTransferMatrix(unitary::DenseOperator{B, B, Matrix{ComplexF64}}) where B <: PauliBasis = PauliTransferMatrix(SuperOperator(unitary)) """ ChiMatrix(unitary::DenseOperator) Convert an operator, presumably a unitary operator, to its representation as a χ matrix. """ -function ChiMatrix(unitary::DenseOperator{B, B, Array{Complex{Float64},2}}) where B <: PauliBasis +function ChiMatrix(unitary::DenseOperator{B, B, Matrix{ComplexF64}}) where B <: PauliBasis num_qubits = length(unitary.basis_l.bases) pbv = pauli_basis_vectors(num_qubits) - aj = pbv' * reshape(unitary.data, 2 ^ (2 * num_qubits)) + aj = pbv' * reshape(unitary.data, 4 ^ num_qubits) return DenseChiMatrix((unitary.basis_l, unitary.basis_l), (unitary.basis_r, unitary.basis_r), aj * aj' / (2 ^ num_qubits)) end @@ -170,11 +174,11 @@ end Convert a superoperator to its representation as a Chi matrix. """ -function ChiMatrix(sop::DenseSuperOperator{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} +function ChiMatrix(sop::DenseSuperOperator{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} num_qubits = length(sop.basis_l) - sop_dim = 2 ^ (2 * num_qubits) + sop_dim = 4 ^ num_qubits po = pauli_operators(num_qubits) - data = Array{Complex{Float64}, 2}(undef, (sop_dim, sop_dim)) + data = Matrix{ComplexF64}(undef, (sop_dim, sop_dim)) for (idx, jdx) in Iterators.product(1:sop_dim, 1:sop_dim) data[idx, jdx] = tr((spre(po[idx]) * spost(po[jdx])).data' * sop.data) / √sop_dim end @@ -186,11 +190,11 @@ end Convert a χ matrix to its representation as a Pauli transfer matrix. """ -function PauliTransferMatrix(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} +function PauliTransferMatrix(chi_matrix::DenseChiMatrix{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} num_qubits = length(chi_matrix.basis_l) - sop_dim = 2 ^ (2 * num_qubits) + sop_dim = 4 ^ num_qubits po = pauli_operators(num_qubits) - data = Array{Float64, 2}(undef, (sop_dim, sop_dim)) + data = Matrix{Float64}(undef, (sop_dim, sop_dim)) for (idx, jdx) in Iterators.product(1:sop_dim, 1:sop_dim) data[idx, jdx] = tr(mapreduce(x -> po[idx] * po[x[1]] * po[jdx] * po[x[2]] * chi_matrix.data[x[1], x[2]], +, @@ -204,7 +208,7 @@ end Convert a χ matrix to its representation as a superoperator. """ -function SuperOperator(chi_matrix::DenseChiMatrix{B, B, Array{Complex{Float64}, 2}}) where B <: Tuple{PauliBasis, PauliBasis} +function SuperOperator(chi_matrix::DenseChiMatrix{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} return SuperOperator(PauliTransferMatrix(chi_matrix)) end @@ -213,13 +217,11 @@ end Convert a Pauli transfer matrix to its representation as a χ matrix. """ -function ChiMatrix(ptm::DensePauliTransferMatrix{B, B, Array{Float64, 2}}) where B <: Tuple{PauliBasis, PauliBasis} +function ChiMatrix(ptm::DensePauliTransferMatrix{B, B, Matrix{Float64}}) where B <: Tuple{PauliBasis, PauliBasis} return ChiMatrix(SuperOperator(ptm)) end -""" -Equality for all varieties of superoperators. -""" +"""Equality for all varieties of superoperators.""" ==(sop1::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}, sop2::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}) = ((typeof(sop1) == typeof(sop2)) & (sop1.basis_l == sop2.basis_l) & diff --git a/test/test_pauli.jl b/test/test_pauli.jl index fc1ee555..5e1870ba 100644 --- a/test/test_pauli.jl +++ b/test/test_pauli.jl @@ -9,6 +9,7 @@ using QuantumOptics # Test conversion of unitary matrices to superoperators. q2 = PauliBasis(2) +q3 = PauliBasis(3) CZ = DenseOperator(q2, q2, diagm(0 => [1,1,1,-1])) CZ_sop = SuperOperator(CZ) @@ -19,17 +20,41 @@ CZ_sop = SuperOperator(CZ) # Test conversion of superoperator to Pauli transfer matrix. CZ_ptm = PauliTransferMatrix(CZ_sop) -CZ_ptm_test = zeros(Float64, (16, 16)) -CZ_ptm_test[[1,30,47,52,72,91,117,140,166,185,205,210,227,256]] = ones(14) -CZ_ptm_test[[106,151]] = -1 * ones(2) +# Test DensePauliTransferMatrix constructor. +@test_throws DimensionMismatch DensePauliTransferMatrix((q2, q2), (q3, q3), CZ_ptm.data) +@test DensePauliTransferMatrix((q2, q2), (q2, q2), CZ_ptm.data) == CZ_ptm + +@test all(isapprox.(CZ_ptm.data[[1,30,47,52,72,91,117,140,166,185,205,210,227,256]], 1)) +@test all(isapprox.(CZ_ptm.data[[106,151]], -1)) -@test CZ_ptm.data == CZ_ptm_test @test CZ_ptm == PauliTransferMatrix(ChiMatrix(CZ)) -# Test conversion among all three bases. -cphase = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 exp(1im*.6)] +# Test construction of non-symmetric unitary. +CNOT = DenseOperator(q2, q2, diagm(0 => [1,1,0,0], 1 => [0,0,1], -1 => [0,0,1])) +CNOT_sop = SuperOperator(CNOT) +CNOT_chi = ChiMatrix(CNOT) +CNOT_ptm = PauliTransferMatrix(CNOT) -q2 = PauliBasis(2) +@test CNOT_sop.basis_l == CNOT_sop.basis_r == (q2, q2) +@test CNOT_chi.basis_l == CNOT_chi.basis_r == (q2, q2) +@test CNOT_ptm.basis_l == CNOT_ptm.basis_r == (q2, q2) + +@test all(isapprox.(imag.(CNOT_sop.data), 0)) +@test all(isapprox.(imag.(CNOT_chi.data), 0)) +@test all(isapprox.(imag.(CNOT_ptm.data), 0)) + +@test all(isapprox.(CNOT_sop.data[[1,18,36,51,69,86,104,119,141,158,176,191,201,218,236,251]], 1)) +@test all(isapprox.(CNOT_chi.data[[1,2,13,17,18,29,193,194,205,222]], 1)) +@test all(isapprox.(CNOT_chi.data[[14,30,206,209,210,221]], -1)) +@test all(isapprox.(CNOT_ptm.data[[1,18,47,64,70,85,108,138,153,183,205,222,227,244,]], 1)) +@test all(isapprox.(CNOT_ptm.data[[123,168]], -1)) + +# Test DenseChiMatrix constructor. +@test_throws DimensionMismatch DenseChiMatrix((q2, q2), (q3, q3), CNOT_chi.data) +@test DenseChiMatrix((q2, q2), (q2, q2), CNOT_chi.data) == CNOT_chi + +# Test equality and conversion among all three bases. +cphase = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 exp(1im*.6)] CPHASE = DenseOperator(q2, cphase) From 187df97caee591793d094f3384946f9a2b0b6a70 Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Thu, 18 Jul 2019 20:27:43 -0700 Subject: [PATCH 5/7] code review and composition for chi and ptm --- src/QuantumOptics.jl | 3 +- src/pauli.jl | 80 ++++++++++++++++++++++++++++++++++++++++++-- src/spin.jl | 12 ------- test/test_pauli.jl | 4 +++ 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/src/QuantumOptics.jl b/src/QuantumOptics.jl index a1c2c4cd..6d166ab5 100644 --- a/src/QuantumOptics.jl +++ b/src/QuantumOptics.jl @@ -35,7 +35,8 @@ export bases, Basis, GenericBasis, CompositeBasis, basis, steadystate, timecorrelations, semiclassical, - stochastic, PauliBasis, PauliTransferMatrix, DensePauliTransferMatrix, + stochastic, + PauliBasis, PauliTransferMatrix, DensePauliTransferMatrix, ChiMatrix, DenseChiMatrix diff --git a/src/pauli.jl b/src/pauli.jl index 02567304..186b03b9 100644 --- a/src/pauli.jl +++ b/src/pauli.jl @@ -4,6 +4,7 @@ export PauliBasis, PauliTransferMatrix, DensePauliTransferMatrix, ChiMatrix, DenseChiMatrix import Base: == +import Base: * using ..bases, ..spin, ..superoperators using ..operators: identityoperator, AbstractOperator @@ -23,10 +24,9 @@ mutable struct PauliBasis{B<:Tuple{Vararg{Basis}}} <: Basis shape::Vector{Int} bases::B function PauliBasis(num_qubits::Int) - type = Tuple{(SpinBasis{1//2} for _ in 1:num_qubits)...} shape = [2 for _ in 1:num_qubits] bases = Tuple(SpinBasis(1//2) for _ in 1:num_qubits) - return new{type}(shape, bases) + return new{typeof(bases)}(shape, bases) end end ==(pb1::PauliBasis, pb2::PauliBasis) = length(pb1.bases) == length(pb2.bases) @@ -61,6 +61,11 @@ end PauliTransferMatrix(ptm::DensePauliTransferMatrix{B, B, Matrix{Float64}}) where B <: Tuple{PauliBasis, PauliBasis} = ptm +function *(ptm0::DensePauliTransferMatrix{B, B, Matrix{Float64}}, + ptm1::DensePauliTransferMatrix{B, B, Matrix{Float64}}) where B <: Tuple{PauliBasis, PauliBasis} + return DensePauliTransferMatrix(ptm0.basis_l, ptm1.basis_r, ptm0.data*ptm1.data) +end + """ Base class for χ (process) matrix classes. """ @@ -90,6 +95,77 @@ end ChiMatrix(chi_matrix::DenseChiMatrix{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} = chi_matrix +""" +A dictionary that represents the Pauli algebra - for a pair of Pauli operators +σᵢσⱼ information about their product is given under the key "ij". The first +element of the dictionary value is the Pauli operator, and the second is the +scalar multiplier. For example, σ₀σ₁ = σ₁, and `"01" => ("1", 1)`. +""" +const pauli_multiplication_dict = Dict( + "00" => ("0", 1.0+0.0im), + "23" => ("1", 0.0+1.0im), + "30" => ("3", 1.0+0.0im), + "22" => ("0", 1.0+0.0im), + "21" => ("3", -0.0-1.0im), + "10" => ("1", 1.0+0.0im), + "31" => ("2", 0.0+1.0im), + "20" => ("2", 1.0+0.0im), + "01" => ("1", 1.0+0.0im), + "33" => ("0", 1.0+0.0im), + "13" => ("2", -0.0-1.0im), + "32" => ("1", -0.0-1.0im), + "11" => ("0", 1.0+0.0im), + "03" => ("3", 1.0+0.0im), + "12" => ("3", 0.0+1.0im), + "02" => ("2", 1.0+0.0im), +) + +""" + multiply_pauli_matirices(i4::String, j4::String) + +A function to algebraically determine result of multiplying two +(N-qubit) Pauli matrices. Each Pauli matrix is represented by a string +in base 4. For example, σ₃⊗σ₀⊗σ₂ would be "302". The product of any pair of +Pauli matrices will itself be a Pauli matrix multiplied by any of the 1/4 roots +of 1. +""" +pauli_multiplication_cache = Dict() +function multiply_pauli_matirices(i4::String, j4::String) + if !((i4, j4) in keys(pauli_multiplication_cache)) + pauli_multiplication_cache[(i4, j4)] = mapreduce(x -> pauli_multiplication_dict[prod(x)], + (x,y) -> (x[1] * y[1], x[2] * y[2]), + zip(i4, j4)) + end + return pauli_multiplication_cache[(i4, j4)] +end + + +function *(chi_matrix0::DenseChiMatrix{B, B, Matrix{ComplexF64}}, + chi_matrix1::DenseChiMatrix{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} + + num_qubits = length(chi_matrix0.basis_l[1].shape) + sop_dim = 2 ^ prod(chi_matrix0.basis_l[1].shape) + ret = zeros(ComplexF64, (sop_dim, sop_dim)) + + for ijkl in Iterators.product(0:(sop_dim-1), + 0:(sop_dim-1), + 0:(sop_dim-1), + 0:(sop_dim-1)) + i, j, k, l = ijkl + if (chi_matrix0.data[i+1, j+1] != 0.0) & (chi_matrix1.data[k+1, l+1] != 0.0) + i4, j4, k4, l4 = map(x -> string(x, base=4, pad=2), ijkl) + + pauli_product_ik = multiply_pauli_matirices(i4, k4) + pauli_product_lj = multiply_pauli_matirices(l4, j4) + + ret[parse(Int, pauli_product_ik[1], base=4)+1, + parse(Int, pauli_product_lj[1], base=4)+1] += (pauli_product_ik[2] * pauli_product_lj[2] * chi_matrix0.data[i+1, j+1] * chi_matrix1.data[k+1, l+1]) + end + end + return DenseChiMatrix(chi_matrix0.basis_l, chi_matrix0.basis_r, ret / 2^num_qubits) +end + + # TODO MAKE A GENERATOR FUNCTION """ pauli_operators(num_qubits::Int) diff --git a/src/spin.jl b/src/spin.jl index b2b662bf..f6fa1e31 100644 --- a/src/spin.jl +++ b/src/spin.jl @@ -34,18 +34,6 @@ SpinBasis(spinnumber::Int) = SpinBasis(convert(Rational{Int}, spinnumber)) ==(b1::SpinBasis, b2::SpinBasis) = b1.spinnumber==b2.spinnumber -""" - identityoperator(b::SpinBasis) - -Pauli ``I`` operator for the given Spin basis. -""" -function identityoperator(b::SpinBasis) - N = length(b) - diag = ComplexF64[complex(1) for m=b.spinnumber:-1:-b.spinnumber] - data = spdiagm(0 => diag) - SparseOperator(b, data) -end - """ sigmax(b::SpinBasis) diff --git a/test/test_pauli.jl b/test/test_pauli.jl index 5e1870ba..8d1ad05f 100644 --- a/test/test_pauli.jl +++ b/test/test_pauli.jl @@ -69,4 +69,8 @@ CPHASE_ptm = PauliTransferMatrix(CPHASE) @test PauliTransferMatrix(CPHASE_sop) == CPHASE_ptm @test PauliTransferMatrix(CPHASE_chi) == CPHASE_ptm +# Test composition. +@test ChiMatrix(CPHASE) * ChiMatrix(CNOT) == ChiMatrix(CPHASE * CNOT) +@test PauliTransferMatrix(CPHASE) * PauliTransferMatrix(CNOT) == PauliTransferMatrix(CPHASE * CNOT) + end # testset From 6acd8b6367eb570e7aa9f746cc8f53000bec1888 Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Fri, 19 Jul 2019 09:29:29 -0700 Subject: [PATCH 6/7] better caching --- src/pauli.jl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/pauli.jl b/src/pauli.jl index 186b03b9..cbeb21c1 100644 --- a/src/pauli.jl +++ b/src/pauli.jl @@ -129,16 +129,18 @@ in base 4. For example, σ₃⊗σ₀⊗σ₂ would be "302". The product of any Pauli matrices will itself be a Pauli matrix multiplied by any of the 1/4 roots of 1. """ -pauli_multiplication_cache = Dict() -function multiply_pauli_matirices(i4::String, j4::String) - if !((i4, j4) in keys(pauli_multiplication_cache)) - pauli_multiplication_cache[(i4, j4)] = mapreduce(x -> pauli_multiplication_dict[prod(x)], - (x,y) -> (x[1] * y[1], x[2] * y[2]), - zip(i4, j4)) +cache_multiply_pauli_matrices() = begin + local pauli_multiplication_cache = Dict() + function _multiply_pauli_matirices(i4::String, j4::String) + if (i4, j4) ∉ keys(pauli_multiplication_cache) + pauli_multiplication_cache[(i4, j4)] = mapreduce(x -> pauli_multiplication_dict[prod(x)], + (x,y) -> (x[1] * y[1], x[2] * y[2]), + zip(i4, j4)) + end + return pauli_multiplication_cache[(i4, j4)] end - return pauli_multiplication_cache[(i4, j4)] end - +multiply_pauli_matirices = cache_multiply_pauli_matrices() function *(chi_matrix0::DenseChiMatrix{B, B, Matrix{ComplexF64}}, chi_matrix1::DenseChiMatrix{B, B, Matrix{ComplexF64}}) where B <: Tuple{PauliBasis, PauliBasis} From 3398cb6302dd39ed49d5a3bbdf2f501e5e672a74 Mon Sep 17 00:00:00 2001 From: alexander papageorge Date: Fri, 26 Jul 2019 08:36:06 -0700 Subject: [PATCH 7/7] code review for isapprox vs == --- src/pauli.jl | 13 ++++++++----- test/test_pauli.jl | 32 ++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/pauli.jl b/src/pauli.jl index cbeb21c1..253cd3b3 100644 --- a/src/pauli.jl +++ b/src/pauli.jl @@ -4,6 +4,7 @@ export PauliBasis, PauliTransferMatrix, DensePauliTransferMatrix, ChiMatrix, DenseChiMatrix import Base: == +import Base: isapprox import Base: * using ..bases, ..spin, ..superoperators @@ -300,10 +301,12 @@ function ChiMatrix(ptm::DensePauliTransferMatrix{B, B, Matrix{Float64}}) where B end """Equality for all varieties of superoperators.""" -==(sop1::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}, - sop2::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}) = ((typeof(sop1) == typeof(sop2)) & - (sop1.basis_l == sop2.basis_l) & - (sop1.basis_r == sop2.basis_r) & - isapprox(sop1.data, sop2.data)) +==(sop1::T, sop2::T) where T<:Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix} = sop1.data == sop2.data +==(sop1::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}, sop2::Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix}) = false + +"""Approximate equality for all varieties of superoperators.""" +function isapprox(sop1::T, sop2::T; kwargs...) where T<:Union{DensePauliTransferMatrix, DenseSuperOperator, DenseChiMatrix} + return isapprox(sop1.data, sop2.data; kwargs...) +end end # end module diff --git a/test/test_pauli.jl b/test/test_pauli.jl index 8d1ad05f..66c51944 100644 --- a/test/test_pauli.jl +++ b/test/test_pauli.jl @@ -54,6 +54,22 @@ CNOT_ptm = PauliTransferMatrix(CNOT) @test DenseChiMatrix((q2, q2), (q2, q2), CNOT_chi.data) == CNOT_chi # Test equality and conversion among all three bases. +ident = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1] + +IDENT = DenseOperator(q2, ident) + +IDENT_sop = SuperOperator(IDENT) +IDENT_chi = ChiMatrix(IDENT) +IDENT_ptm = PauliTransferMatrix(IDENT) + +@test ChiMatrix(IDENT_sop) == IDENT_chi +@test ChiMatrix(IDENT_ptm) == IDENT_chi +@test SuperOperator(IDENT_chi) == IDENT_sop +@test SuperOperator(IDENT_ptm) == IDENT_sop +@test PauliTransferMatrix(IDENT_sop) == IDENT_ptm +@test PauliTransferMatrix(IDENT_chi) == IDENT_ptm + +# Test approximate equality and conversion among all three bases. cphase = Complex{Float64}[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 exp(1im*.6)] CPHASE = DenseOperator(q2, cphase) @@ -62,15 +78,15 @@ CPHASE_sop = SuperOperator(CPHASE) CPHASE_chi = ChiMatrix(CPHASE) CPHASE_ptm = PauliTransferMatrix(CPHASE) -@test ChiMatrix(CPHASE_sop) == CPHASE_chi -@test ChiMatrix(CPHASE_ptm) == CPHASE_chi -@test SuperOperator(CPHASE_chi) == CPHASE_sop -@test SuperOperator(CPHASE_ptm) == CPHASE_sop -@test PauliTransferMatrix(CPHASE_sop) == CPHASE_ptm -@test PauliTransferMatrix(CPHASE_chi) == CPHASE_ptm +@test isapprox(ChiMatrix(CPHASE_sop), CPHASE_chi) +@test isapprox(ChiMatrix(CPHASE_ptm), CPHASE_chi) +@test isapprox(SuperOperator(CPHASE_chi), CPHASE_sop) +@test isapprox(SuperOperator(CPHASE_ptm), CPHASE_sop) +@test isapprox(PauliTransferMatrix(CPHASE_sop), CPHASE_ptm) +@test isapprox(PauliTransferMatrix(CPHASE_chi), CPHASE_ptm) # Test composition. -@test ChiMatrix(CPHASE) * ChiMatrix(CNOT) == ChiMatrix(CPHASE * CNOT) -@test PauliTransferMatrix(CPHASE) * PauliTransferMatrix(CNOT) == PauliTransferMatrix(CPHASE * CNOT) +@test isapprox(ChiMatrix(CPHASE) * ChiMatrix(CNOT), ChiMatrix(CPHASE * CNOT)) +@test isapprox(PauliTransferMatrix(CPHASE) * PauliTransferMatrix(CNOT), PauliTransferMatrix(CPHASE * CNOT)) end # testset