diff --git a/Project.toml b/Project.toml index b505d9d..9e2539d 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ PrecompileTools = "1" QuantumClifford = "0.8" QuantumInterface = "0.3.0" QuantumOptics = "1.0.5" -QuantumOpticsBase = "0.4" +QuantumOpticsBase = "0.4.16" QuantumSymbolics = "0.2.4" Reexport = "1.2.2" ResumableFunctions = "0.6.1" diff --git a/src/CircuitZoo/CircuitZoo.jl b/src/CircuitZoo/CircuitZoo.jl index 0e0b91a..114fb96 100644 --- a/src/CircuitZoo/CircuitZoo.jl +++ b/src/CircuitZoo/CircuitZoo.jl @@ -4,7 +4,8 @@ using QuantumSavory using DocStringExtensions export EntanglementSwap, Purify2to1, Purify2to1Node, Purify3to1, Purify3to1Node, - PurifyStringent, PurifyStringentNode, PurifyExpedient, PurifyExpedientNode + PurifyStringent, PurifyStringentNode, PurifyExpedient, PurifyExpedientNode, + SDDecode, SDEncode abstract type AbstractCircuit end @@ -851,4 +852,77 @@ function (circuit::PurifyExpedientNode)(purified,sacrificed...) [a..., b..., c..., d...] end +""" +$TYPEDEF + +Fields: + +$FIELDS + +The circuit for [Superdense Coding](https://en.wikipedia.org/wiki/Superdense_coding) to encode the 2 (classical) bit message +to its corresponding Bell pair representation. It takes as argumes a single qubit register containing +Alice's half of the entangled Bell pair and the 2 bit message Alice intends to send to Bob. + +```jldoctest +julia> regA = Register(1); regB = Register(2); + +julia> initialize!((regA[1], regB[1]), (L0⊗L0+L1⊗L1)/√2); + +julia> message = [1, 1]; + +julia> SDEncode()(regA, message); +``` + +See also [`SDDecode`](@ref) +""" +struct SDEncode <: AbstractCircuit +end + +function (circuit::SDEncode)(reg, message) + if message[2] == 1 + apply!(reg[1], X) + end + if message[1] == 1 + apply!(reg[1], Z) + end +end + + +""" +$TYPEDEF + +Fields: + +$FIELDS + +The circuit for Superdense Coding to decode the 2 (classical) bit message +using the entangled bell pair stored in the registers regA and regB after Alice's encoding of the first qubit. +Returns a Tuple of the decoded message. + +```jldoctest +julia> regA = Register(1); regB = Register(2); + +julia> initialize!((regA[1], regB[1]), (L0⊗L0+L1⊗L1)/√2); + +julia> message = [1, 1]; + +julia> SDEncode()(regA, message); + +julia> SDDecode()(regA, regB) +(1, 1) +``` + +See also [`SDEncode`](@ref) +""" +struct SDDecode <: AbstractCircuit +end + +function (circuit::SDDecode)(regA, regB) + apply!((regA[1], regB[1]), CNOT) + apply!(regA[1], H) + b1 = project_traceout!(regA, 1, Z) + b2 = project_traceout!(regB, 1, Z) + return b1-1, b2-1 +end + end # module diff --git a/src/backends/quantumoptics/quantumoptics.jl b/src/backends/quantumoptics/quantumoptics.jl index 6ec699f..d2b081b 100644 --- a/src/backends/quantumoptics/quantumoptics.jl +++ b/src/backends/quantumoptics/quantumoptics.jl @@ -37,6 +37,7 @@ function observable(state::Union{<:Ket,<:Operator}, indices, operation) expect(op, state) end + function project_traceout!(state::Union{Ket,Operator},stateindex,psis::Base.AbstractVecOrTuple{<:Ket}) if nsubsystems(state) == 1 # TODO is there a way to do this in a single function, instead of _overlap vs _project_and_drop _overlaps = [_overlap(psi,state) for psi in psis] diff --git a/test/runtests.jl b/test/runtests.jl index 1b1e3c0..9bc2c8f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,6 +28,7 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA @doset "observable" @doset "noninstant_and_backgrounds_qubit" @doset "noninstant_and_backgrounds_qumode" +@doset "circuitzoo_superdense" @doset "circuitzoo_purification" @doset "examples" get(ENV,"QUANTUMSAVORY_PLOT_TEST","")=="true" && @doset "plotting_cairo" diff --git a/test/test_circuitzoo_purification.jl b/test/test_circuitzoo_purification.jl index 2005cbd..0977ffa 100644 --- a/test/test_circuitzoo_purification.jl +++ b/test/test_circuitzoo_purification.jl @@ -104,19 +104,15 @@ end r = Register(6, rep()) initialize!(r[1:6], bell⊗bell⊗bell) - @testset "$leaveout1, $leaveout2" begin - @test Purify3to1(leaveout1, leaveout2)(r[1], r[2], r[3], r[5], r[4], r[6])==true - @test observable(r[1:2], projector(bell))≈1.0 - end + @test Purify3to1(leaveout1, leaveout2)(r[1], r[2], r[3], r[5], r[4], r[6])==true + @test observable(r[1:2], projector(bell))≈1.0 for error in [:X, :Y, :Z], target in 3:6 r = Register(6, rep()) for i in 1:3 initialize!(r[(2*i-1):(2*i)], bell) end apply!(r[target], Dict(:X=>X, :Y=>Y, :Z=>Z)[error]) - @testset "error: $leaveout1, $leaveout2" begin - @test Purify3to1(leaveout1, leaveout2)(r[1], r[2], r[3], r[5], r[4], r[6])==false - end + @test Purify3to1(leaveout1, leaveout2)(r[1], r[2], r[3], r[5], r[4], r[6])==false end for error in [:X, :Y, :Z], target in 1:2 r = Register(6, rep()) diff --git a/test/test_circuitzoo_superdense.jl b/test/test_circuitzoo_superdense.jl new file mode 100644 index 0000000..db3bb35 --- /dev/null +++ b/test/test_circuitzoo_superdense.jl @@ -0,0 +1,24 @@ +using QuantumSavory +using QuantumSavory.CircuitZoo: SDEncode, SDDecode +using Test + +for i in 1:8 + ## Set up an entangled bell pair + ra = Register(1) + rb = Register(1) + + initialize!(ra[1], Z1) + initialize!(rb[1], Z1) + + apply!(ra[1], H) + apply!((ra[1], rb[1]), CNOT) + + # Random 2 bit classical message + message = Tuple(rand(0:1, 2)) + + # Use the circuits to encode and decode the message + SDEncode()(ra, message) + rec = SDDecode()(ra, rb) + + @test message == rec +end