diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index 09466cde..1226a017 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -47,7 +47,7 @@ Tag(tag::EntanglementHistory) = Tag(EntanglementHistory, tag.remote_node, tag.re new_remote_slot::Int correction::Int end -Base.show(io::IO, tag::EntanglementUpdateX) = print(io, "Update slot .$(tag.past_remote_slot) which used to be entangled to $(tag.past_local_node).$(tag.past_local_slot) to be entangled to $(tag.new_remote_node).$(tag.new_remote_slot) and apply correction X$(tag.correction)") +Base.show(io::IO, tag::EntanglementUpdateX) = print(io, "Update slot .$(tag.past_remote_slot) which used to be entangled to $(tag.past_local_node).$(tag.past_local_slot) to be entangled to $(tag.new_remote_node).$(tag.new_remote_slot) and apply correction Z$(tag.correction)") Tag(tag::EntanglementUpdateX) = Tag(EntanglementUpdateX, tag.past_local_node, tag.past_local_slot, tag.past_remote_slot, tag.new_remote_node, tag.new_remote_slot, tag.correction) @kwdef struct EntanglementUpdateZ @@ -58,7 +58,7 @@ Tag(tag::EntanglementUpdateX) = Tag(EntanglementUpdateX, tag.past_local_node, ta new_remote_slot::Int correction::Int end -Base.show(io::IO, tag::EntanglementUpdateZ) = print(io, "Update slot .$(tag.past_remote_slot) which used to be entangled to $(tag.past_local_node).$(tag.past_local_slot) to be entangled to $(tag.new_remote_node).$(tag.new_remote_slot) and apply correction Z$(tag.correction)") +Base.show(io::IO, tag::EntanglementUpdateZ) = print(io, "Update slot .$(tag.past_remote_slot) which used to be entangled to $(tag.past_local_node).$(tag.past_local_slot) to be entangled to $(tag.new_remote_node).$(tag.new_remote_slot) and apply correction X$(tag.correction)") Tag(tag::EntanglementUpdateZ) = Tag(EntanglementUpdateZ, tag.past_local_node, tag.past_local_slot, tag.past_remote_slot, tag.new_remote_node, tag.new_remote_slot, tag.correction) """ @@ -249,7 +249,7 @@ end workwasdone = true # waiting is not enough because we might have multiple rounds of work to do while workwasdone workwasdone = false - for (updatetagsymbol, updategate) in ((EntanglementUpdateX, X), (EntanglementUpdateZ, Z)) + for (updatetagsymbol, updategate) in ((EntanglementUpdateX, Z), (EntanglementUpdateZ, X)) # look for EntanglementUpdate? past_remote_slot_idx local_slot_idx, new_remote_node, new_remote_slot_idx correction msg = querydelete!(mb, updatetagsymbol, ❓, ❓, ❓, ❓, ❓, ❓) isnothing(msg) && continue @@ -272,7 +272,10 @@ end unlock(localslot) error("There was an error in the entanglement tracking protocol `EntanglementTracker`. We were attempting to forward a classical message from a node that performed a swap to the remote entangled node. However, on reception of that message it was found that the remote node has lost track of its part of the entangled state although it still keeps a `Tag` as a record of it being present.") end - # TODO correction gate + # Pauli frame correction gate + if correction==2 + apply!(localslot, updategate) + end # tag local with updated EntanglementCounterpart new_remote_node new_remote_slot_idx tag!(localslot, EntanglementCounterpart, newremotenode, newremoteslotid) unlock(localslot) @@ -291,7 +294,6 @@ end @debug "EntanglementTracker @$(prot.node): history=`$(history)` | message=`$msg` | Sending to $(whoweswappedwith_node).$(whoweswappedwith_slotidx)" msghist = Tag(updatetagsymbol, pastremotenode, pastremoteslotid, whoweswappedwith_slotidx, newremotenode, newremoteslotid, correction) put!(channel(prot.net, prot.node=>whoweswappedwith_node; permit_forward=true), msghist) - #println(" history sends to $whoweswappedwith_node: ", msghist) continue end error("`EntanglementTracker` on node $(prot.node) received a message $(msg) that it does not know how to handle (due to the absence of corresponding `EntanglementCounterpart` or `EntanglementHistory` tags). This is a bug in the protocol and should not happen -- please report an issue at QuantumSavory's repository.") diff --git a/test/runtests.jl b/test/runtests.jl index 02dce471..7fc4b624 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,6 +34,7 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA @doset "entanglement_tracker" @doset "circuitzoo_api" +@doset "circuitzoo_ent_swap" @doset "circuitzoo_purification" @doset "circuitzoo_superdense" diff --git a/test/test_circuitzoo_ent_swap.jl b/test/test_circuitzoo_ent_swap.jl new file mode 100644 index 00000000..26b165c7 --- /dev/null +++ b/test/test_circuitzoo_ent_swap.jl @@ -0,0 +1,57 @@ +using QuantumSavory +using QuantumSavory.CircuitZoo +using Test +using QuantumSavory.CircuitZoo: EntanglementSwap, LocalEntanglementSwap + +const perfect_pair_stab = StabilizerState("XX ZZ") +const perfect_pair = (Z1⊗Z1 + Z2⊗Z2) / sqrt(2) + +for pair in (perfect_pair, perfect_pair_stab), rep in 1:10 + net = RegisterNet([Register(1), Register(2), Register(1)]) + initialize!((net[1][1], net[2][1]), pair) + initialize!((net[3][1], net[2][2]), pair) + EntanglementSwap()(net[2][1], net[1][1], net[2][2], net[3][1]) + @test !isassigned(net[2][1]) && !isassigned(net[2][2]) + @test observable((net[1][1], net[3][1]), Z⊗Z) ≈ 1 + @test observable((net[1][1], net[3][1]), X⊗X) ≈ 1 +end + +for pair in (perfect_pair, perfect_pair_stab), rep in 1:10 + net = RegisterNet([Register(1), Register(2), Register(1)]) + initialize!((net[1][1], net[2][1]), pair) + initialize!((net[3][1], net[2][2]), pair) + mx, mz = LocalEntanglementSwap()(net[2][1], net[2][2]) + mx == 2 && apply!(net[1][1], Z) + mz == 2 && apply!(net[3][1], X) + @test !isassigned(net[2][1]) && !isassigned(net[2][2]) + @test observable((net[1][1], net[3][1]), Z⊗Z) ≈ 1 + @test observable((net[1][1], net[3][1]), X⊗X) ≈ 1 +end + +for pair in (perfect_pair, perfect_pair_stab), n in 3:10, rep in 1:10 + net = RegisterNet([Register(2) for i in 1:n]) + for i in 1:n-1 + initialize!((net[i][1], net[i+1][2]), pair) + end + for i in 2:n-1 + EntanglementSwap()(net[i][2], net[1][1], net[i][1], net[i+1][2]) + end + @test all(!isassigned(net[i][1]) & !isassigned(net[i][2]) for i in 2:n-1) + @test observable((net[1][1], net[n][2]), Z⊗Z) ≈ 1 + @test observable((net[1][1], net[n][2]), X⊗X) ≈ 1 +end + +for pair in (perfect_pair, perfect_pair_stab), n in 3:10, rep in 1:10 + net = RegisterNet([Register(2) for i in 1:n]) + for i in 1:n-1 + initialize!((net[i][1], net[i+1][2]), pair) + end + for i in 2:n-1 + mx, mz = LocalEntanglementSwap()(net[i][2], net[i][1]) + mx == 2 && apply!(net[1][1], Z) + mz == 2 && apply!(net[i+1][2], X) + end + @test all(!isassigned(net[i][1]) & !isassigned(net[i][2]) for i in 2:n-1) + @test observable((net[1][1], net[n][2]), Z⊗Z) ≈ 1 + @test observable((net[1][1], net[n][2]), X⊗X) ≈ 1 +end diff --git a/test/test_entanglement_tracker.jl b/test/test_entanglement_tracker.jl index 7d4a493b..3698fd06 100644 --- a/test/test_entanglement_tracker.jl +++ b/test/test_entanglement_tracker.jl @@ -16,51 +16,55 @@ end ## -net = RegisterNet([Register(3), Register(4), Register(2), Register(3)]) -sim = get_time_tracker(net) +# without an entanglement tracker +for i in 1:10 + + net = RegisterNet([Register(3), Register(4), Register(2), Register(3)]) + sim = get_time_tracker(net) -entangler1 = EntanglerProt(sim, net, 1, 2; rounds=1) -@process entangler1() -run(sim, 20) -@test net[1].tags == [[Tag(EntanglementCounterpart, 2, 1)],[],[]] + entangler1 = EntanglerProt(sim, net, 1, 2; rounds=1) + @process entangler1() + run(sim, 20) + @test net[1].tags == [[Tag(EntanglementCounterpart, 2, 1)],[],[]] -entangler2 = EntanglerProt(sim, net, 2, 3; rounds=1) -@process entangler2() -run(sim, 40) -entangler3 = EntanglerProt(sim, net, 4, 3; rounds=1) -@process entangler3() -run(sim, 60) -@test net[1].tags == [[Tag(EntanglementCounterpart, 2, 1)],[],[]] -@test net[2].tags == [[Tag(EntanglementCounterpart, 1, 1)],[Tag(EntanglementCounterpart, 3, 1)],[],[]] -@test net[3].tags == [[Tag(EntanglementCounterpart, 2, 2)],[Tag(EntanglementCounterpart, 4, 1)]] -@test net[4].tags == [[Tag(EntanglementCounterpart, 3, 2)],[],[]] + entangler2 = EntanglerProt(sim, net, 2, 3; rounds=1) + @process entangler2() + run(sim, 40) + entangler3 = EntanglerProt(sim, net, 4, 3; rounds=1) + @process entangler3() + run(sim, 60) -@test [islocked(ref) for i in vertices(net) for ref in net[i]] |> any == false + @test net[1].tags == [[Tag(EntanglementCounterpart, 2, 1)],[],[]] + @test net[2].tags == [[Tag(EntanglementCounterpart, 1, 1)],[Tag(EntanglementCounterpart, 3, 1)],[],[]] + @test net[3].tags == [[Tag(EntanglementCounterpart, 2, 2)],[Tag(EntanglementCounterpart, 4, 1)]] + @test net[4].tags == [[Tag(EntanglementCounterpart, 3, 2)],[],[]] + @test [islocked(ref) for i in vertices(net) for ref in net[i]] |> any == false -swapper2 = SwapperProt(sim, net, 2; rounds=1) -swapper3 = SwapperProt(sim, net, 3; rounds=1) -@process swapper2() -@process swapper3() -run(sim, 80) -# In the absence of an entanglement tracker the tags will not all be updated -@test net[1].tags == [[Tag(EntanglementCounterpart, 2, 1)],[],[]] -@test net[2].tags == [[Tag(EntanglementHistory, 1, 1, 3, 1, 2)],[Tag(EntanglementHistory, 3, 1, 1, 1, 1)],[],[]] -@test net[3].tags == [[Tag(EntanglementHistory, 2, 2, 4, 1, 2)],[Tag(EntanglementHistory, 4, 1, 2, 2, 1)]] -@test net[4].tags == [[Tag(EntanglementCounterpart, 3, 2)],[],[]] + swapper2 = SwapperProt(sim, net, 2; rounds=1) + swapper3 = SwapperProt(sim, net, 3; rounds=1) + @process swapper2() + @process swapper3() + run(sim, 80) -@test isassigned(net[1][1]) && isassigned(net[4][1]) -#@test observable((net[1][1], net[4][1]), Z⊗Z) ≈ 1 -@test !isassigned(net[2][1]) && !isassigned(net[3][1]) -@test !isassigned(net[2][2]) && !isassigned(net[3][2]) + # In the absence of an entanglement tracker the tags will not all be updated + @test net[1].tags == [[Tag(EntanglementCounterpart, 2, 1)],[],[]] + @test net[2].tags == [[Tag(EntanglementHistory, 1, 1, 3, 1, 2)],[Tag(EntanglementHistory, 3, 1, 1, 1, 1)],[],[]] + @test net[3].tags == [[Tag(EntanglementHistory, 2, 2, 4, 1, 2)],[Tag(EntanglementHistory, 4, 1, 2, 2, 1)]] + @test net[4].tags == [[Tag(EntanglementCounterpart, 3, 2)],[],[]] -@test [islocked(ref) for i in vertices(net) for ref in net[i]] |> any == false + @test isassigned(net[1][1]) && isassigned(net[4][1]) + @test !isassigned(net[2][1]) && !isassigned(net[3][1]) + @test !isassigned(net[2][2]) && !isassigned(net[3][2]) + @test [islocked(ref) for i in vertices(net) for ref in net[i]] |> any == false + +end ## @@ -76,15 +80,16 @@ using Random if isinteractive() using Logging - logger = ConsoleLogger(Logging.Debug; meta_formatter=(args...)->(:black,"","")) + logger = ConsoleLogger(Logging.Warn; meta_formatter=(args...)->(:black,"","")) global_logger(logger) println("Logger set to debug") end -## same but this time with an entanglement tracker +## + +# same but this time with an entanglement tracker -for i in 1:1000 - n = rand(2:30) +for i in 1:30, n in 2:30 #@show n, i net = RegisterNet([Register(j+3) for j in 1:n]) sim = get_time_tracker(net) @@ -102,7 +107,10 @@ for i in 1:1000 end run(sim, 200) - @test query(net[1], EntanglementCounterpart, n, ❓).tag[2] == n - @test query(net[n], EntanglementCounterpart, 1, ❓).tag[2] == 1 - # @test observable((net[1][1], net[n][1]), Z⊗Z) ≈ 1 + q1 = query(net[1], EntanglementCounterpart, n, ❓) + q2 = query(net[n], EntanglementCounterpart, 1, ❓) + @test q1.tag[2] == n + @test q2.tag[2] == 1 + @test observable((q1.slot, q2.slot), Z⊗Z) ≈ 1 + @test observable((q1.slot, q2.slot), X⊗X) ≈ 1 end