Skip to content

Commit

Permalink
update EntanglementHistory when skipping over a node, randomize tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ba2tro committed Dec 23, 2023
1 parent b6cfaf8 commit 1f58d09
Show file tree
Hide file tree
Showing 13 changed files with 73 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmark-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@v4

# restore records from the artifacts
- uses: dawidd6/action-download-artifact@v2
- uses: dawidd6/action-download-artifact@v3
with:
workflow: benchmark.yml
name: performance-tracking
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
run: echo ${{ github.event.pull_request.number }} > ./pull-request-number.artifact

# save as artifacts (performance tracking (comment) workflow will use it)
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: performance-tracking
path: ./*.artifact
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Abhishek Bhatt
Copyright (c) 2023 Stefan Krastanov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5"
QuantumOptics = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c"
QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678"
QuantumSymbolics = "efa7fd63-0460-4890-beb7-be1bbdfbaeae"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Expand Down Expand Up @@ -48,6 +49,7 @@ QuantumInterface = "0.3.3"
QuantumOptics = "1.0.5"
QuantumOpticsBase = "0.4.17"
QuantumSymbolics = "0.2.5"
Random = "1.9"
Reexport = "1.2.2"
ResumableFunctions = "0.6.1"
Statistics = "1"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<td>Continuous integration</td>
<td>
<a href="https://github.com/QuantumSavory/QuantumSavory.jl/actions?query=workflow%3ACI+branch%3Amaster"><img src="https://github.com/QuantumSavory/QuantumSavory.jl/actions/workflows/ci.yml/badge.svg" alt="GitHub Workflow Status"></a>
<a href="https://buildkite.com/quantumsavory/ci-buildkite?branch=master"><img src="https://badge.buildkite.com/2713c9978db76235fbe98094b7cad9a05f04671fd442739c47.svg?branch=master" alt="Buildkite Workflow Status"></a>
<a href="https://buildkite.com/quantumsavory/quantumsavory"><img src="https://badge.buildkite.com/2713c9978db76235fbe98094b7cad9a05f04671fd442739c47.svg" alt="Buildkite Workflow Status"></a>
</td>
</tr><tr></tr>
<tr>
Expand Down
47 changes: 27 additions & 20 deletions src/ProtocolZoo/ProtocolZoo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Process(prot::AbstractProtocol, args...; kwargs...) = Process((e,a...;k...)->pro
remote_node::Int
remote_slot::Int
end
Base.show(io::IO, tag::EntanglementCounterpart) = print(io, "Entangled to $(tag.remote_node)|$(tag.remote_slot)")
Base.show(io::IO, tag::EntanglementCounterpart) = print(io, "Entangled to $(tag.remote_node).$(tag.remote_slot)")
Tag(tag::EntanglementCounterpart) = Tag(EntanglementCounterpart, tag.remote_node, tag.remote_slot)

@kwdef struct EntanglementHistory
Expand All @@ -36,7 +36,7 @@ Tag(tag::EntanglementCounterpart) = Tag(EntanglementCounterpart, tag.remote_node
swap_remote_slot::Int
swapped_local::Int
end
Base.show(io::IO, tag::EntanglementHistory) = print(io, "Was entangled to $(tag.remote_node)|$(tag.remote_slot), but swapped with |$(tag.swapped_local) which was entangled to $(tag.swap_remote_node)|$(tag.swap_remote_slot)")
Base.show(io::IO, tag::EntanglementHistory) = print(io, "Was entangled to $(tag.remote_node).$(tag.remote_slot), but swapped with .$(tag.swapped_local) which was entangled to $(tag.swap_remote_node).$(tag.swap_remote_slot)")
Tag(tag::EntanglementHistory) = Tag(EntanglementHistory, tag.remote_node, tag.remote_slot, tag.swap_remote_node, tag.swap_remote_slot, tag.swapped_local)

@kwdef struct EntanglementUpdateX
Expand All @@ -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 X$(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
Expand All @@ -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 Z$(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)

"""
Expand Down Expand Up @@ -93,6 +93,8 @@ $FIELDS
retry_lock_time::LT = 0.1
"""how many rounds of this protocol to run (`-1` for infinite))"""
rounds::Int = -1
"""whether the protocol should find the first available free slots in the nodes to be entangled or check for free slots randomly from the available slots"""
randomize::Bool = false
end

"""Convenience constructor for specifying `rate` of generation instead of success probability and time"""
Expand All @@ -110,10 +112,10 @@ end
prot = _prot # weird workaround for no support for `struct A a::Int end; @resumable function (fa::A) return fa.a end`; see https://github.com/JuliaDynamics/ResumableFunctions.jl/issues/77
rounds = prot.rounds
while rounds != 0
a = findfreeslot(prot.net[prot.nodeA])
b = findfreeslot(prot.net[prot.nodeB])
a = findfreeslot(prot.net[prot.nodeA], randomize=prot.randomize)
b = findfreeslot(prot.net[prot.nodeB], randomize=prot.randomize)
if isnothing(a) || isnothing(b)
isnothing(prot.retry_lock_time) && error("we do not yet support waiting on register to make qubits available") # TODO
isnothing(prot.retry_lock_time) && error("We do not yet support waiting on register to make qubits available") # TODO
@yield timeout(prot.sim, prot.retry_lock_time)
continue
end
Expand Down Expand Up @@ -175,7 +177,7 @@ end
reg = prot.net[prot.node]
qubit_pair = findswapablequbits(prot.net,prot.node)
if isnothing(qubit_pair)
isnothing(prot.retry_lock_time) && error("we do not yet support waiting on register to make qubits available") # TODO
isnothing(prot.retry_lock_time) && error("We do not yet support waiting on register to make qubits available") # TODO
@yield timeout(prot.sim, prot.retry_lock_time)
continue
end
Expand All @@ -198,12 +200,12 @@ end
# tag with EntanglementUpdateX past_local_node, past_local_slot_idx past_remote_slot_idx new_remote_node, new_remote_slot, correction
msg1 = Tag(EntanglementUpdateX, prot.node, q1.idx, tag1[3], tag2[2], tag2[3], xmeas)
put!(channel(prot.net, prot.node=>tag1[2]; permit_forward=true), msg1)
@debug "swapper @$(prot.node) send msg to $(tag1[2]): $msg1"
@debug "SwapperProt @$(prot.node): Send message to $(tag1[2]) | message=`$msg1`"
# send from here to new entanglement counterpart:
# tag with EntanglementUpdateZ past_local_node, past_local_slot_idx past_remote_slot_idx new_remote_node, new_remote_slot, correction
msg2 = Tag(EntanglementUpdateZ, prot.node, q2.idx, tag2[3], tag1[2], tag1[3], zmeas)
put!(channel(prot.net, prot.node=>tag2[2]; permit_forward=true), msg2)
@debug "swapper @$(prot.node) send msg to $(tag2[2]): $msg2"
@debug "SwapperProt @$(prot.node): Send message to $(tag2[2]) | message=`$msg2`"
unlock(q1)
unlock(q2)
rounds==-1 || (rounds -= 1)
Expand Down Expand Up @@ -249,20 +251,23 @@ end
workwasdone = false
for (updatetagsymbol, updategate) in ((EntanglementUpdateX, X), (EntanglementUpdateZ, Z))
# look for EntanglementUpdate? past_remote_slot_idx local_slot_idx, new_remote_node, new_remote_slot_idx correction
msg = querypop!(mb, updatetagsymbol, ❓, ❓, ❓, ❓, ❓, ❓)
msg = querydelete!(mb, updatetagsymbol, ❓, ❓, ❓, ❓, ❓, ❓)
isnothing(msg) && continue
@debug "tracker @$(prot.node) received from $(msg.src) msg: $(msg.tag)"
@debug "EntanglementTracker @$(prot.node): Received from $(msg.src).$(msg.tag[3]) | message=`$(msg.tag)`"
workwasdone = true
(src, (_, pastremotenode, pastremoteslotid, localslotid, newremotenode, newremoteslotid, correction)) = msg
localslot = nodereg[localslotid]
# Check if the local slot is still present and believed to be entangled.
# We will need to perform a correction operation due to the swap,
# but there will be no message forwarding necessary.
counterpart = querypop!(localslot, EntanglementCounterpart, pastremotenode, pastremoteslotid)
counterpart = querydelete!(localslot, EntanglementCounterpart, pastremotenode, pastremoteslotid)
if !isnothing(counterpart)
@debug "tracker @$(prot.node) counterpart requesting lock at $(now(prot.sim))"
time_before_lock = now(prot.sim)
@debug "EntanglementTracker @$(prot.node): EntanglementCounterpart requesting lock at $(now(prot.sim))"
@yield lock(localslot)
@debug "tracker @$(prot.node) counterpart getting lock at $(now(prot.sim))"
@debug "EntanglementTracker @$(prot.node): EntanglementCounterpart getting lock at $(now(prot.sim))"
time_after_lock = now(prot.sim)
time_before_lock != time_after_lock && @debug "EntanglementTracker @$(prot.node): Needed Δt=$(time_after_lock-time_before_lock) to get a lock"
if !isassigned(localslot)
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.")
Expand All @@ -275,24 +280,26 @@ end
end
# If not, check if we have a record of the entanglement being swapped to a different remote node,
# and forward the message to that node.
history = querypop!(localslot, EntanglementHistory,
history = querydelete!(localslot, EntanglementHistory,
pastremotenode, pastremoteslotid, # who we were entangled to (node, slot)
❓, ❓, # who we swapped with (node, slot)
❓) # which local slot used to be entangled with whom we swapped with
if !isnothing(history)
@debug "tracker @$(prot.node) history: $(history) | msg: $msg"
# @debug "tracker @$(prot.node) history: $(history) | msg: $msg"
_, _, _, whoweswappedwith_node, whoweswappedwith_slotidx, swappedlocal_slotidx = history
msghist = Tag(updatetagsymbol, pastremotenode, swappedlocal_slotidx, whoweswappedwith_slotidx, newremotenode, newremoteslotid, correction)
tag!(localslot, EntanglementHistory, newremotenode, newremoteslotid, whoweswappedwith_node, whoweswappedwith_slotidx, swappedlocal_slotidx)
@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.")
end
end
#println("tracker @$(prot.node) starting message wait at ", now(prot.sim), " with mb: ", mb.buffer)
@debug "EntanglementTracker @$(prot.node): Starting message wait at $(now(prot.sim)) with MessageBuffer containing: $(mb.buffer)"
@yield wait(mb)
#println("tracker @$(prot.node) message wait ends at ", now(prot.sim))
@debug "EntanglementTracker @$(prot.node): Message wait ends at $(now(prot.sim))"
end
end

Expand Down
3 changes: 2 additions & 1 deletion src/QuantumSavory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ using Reexport

using IterTools
using LinearAlgebra
using Random: randperm
using Graphs
import ConcurrentSim
using ConcurrentSim: Environment, Simulation, Store, DelayQueue, Resource,
Expand Down Expand Up @@ -47,7 +48,7 @@ export
# uptotime.jl
uptotime!, overwritetime!,
# tags.jl and queries.jl
Tag, tag!, untag!, W, ❓, query, queryall, querypop!, findfreeslot,
Tag, tag!, untag!, W, ❓, query, queryall, querydelete!, findfreeslot,
# quantumchannel.jl
QuantumChannel,
# backgrounds.jl
Expand Down
5 changes: 5 additions & 0 deletions src/messagebuffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ end
function Base.put!(cf::ChannelForwarder, tag::Tag)
# shortest path calculated by Graphs.a_star
nexthop = first(a_star(cf.net.graph, cf.src, cf.dst))
@debug "ChannelForwarder: Forwarding message from node $(nexthop.src) to node $(nexthop.dst) | message=$(tag)| end destination=$(cf.dst)"
put!(channel(cf.net, cf.src=>nexthop.dst; permit_forward=false), tag_types.Forward(tag, cf.dst))
end

Expand All @@ -23,10 +24,13 @@ end
tag = @yield take!(ch)
@cases tag begin
Forward(innertag, enddestination) => begin # inefficient -- it recalculates the a_star at each hop TODO provide some caching mechanism
@debug "MessageBuffer @$(mb.node) at t=$(now(mb.sim)): Forwarding message to node $(enddestination) | message=`$(tag)`"
put!(channel(mb.net, mb.node=>enddestination; permit_forward=true), innertag)
end
_ => begin
#println("from $src storing in mb: $tag at $(now(sim))")
@debug "MessageBuffer @$(mb.node) at t=$(now(mb.sim)): Receiving from source $(src) | message=`$(tag)`"
length(mb.waiters) == 0 && @debug "MessageBuffer @$(mb.node) received a message, but there is no one waiting on that message buffer. The message was `$(tag)`."
push!(mb.buffer, (;src,tag));
for waiter in keys(mb.waiters)
unlock(waiter)
Expand All @@ -47,6 +51,7 @@ function MessageBuffer(net, node::Int, qs::Vector{NamedTuple{(:src,:channel), Tu
end

@resumable function wait_process(sim, mb::MessageBuffer)
length(mb.buffer) != 0 && return
waitresource = Resource(sim)
lock(waitresource)
mb.waiters[waitresource] = waitresource
Expand Down
12 changes: 9 additions & 3 deletions src/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,12 @@ t=1.0: query returns nothing
t=3.0: query returns SymbolIntInt(:second_tag, 123, 456)::Tag received from node 3
```
"""
function querypop!(mb::MessageBuffer, args...)
function querydelete!(mb::MessageBuffer, args...)
r = query(mb, args...)
return isnothing(r) ? nothing : popat!(mb.buffer, r.depth)
end

function querypop!(ref::RegRef, args...) # TODO there is a lot of code duplication here
function querydelete!(ref::RegRef, args...) # TODO there is a lot of code duplication here
r = query(ref, args...)
return isnothing(r) ? nothing : popat!(ref.reg.tags[ref.idx], r.depth)
end
Expand Down Expand Up @@ -317,7 +317,13 @@ julia> findfreeslot(reg) |> isnothing
true
```
"""
function findfreeslot(reg::Register)
function findfreeslot(reg::Register; randomize=false)
if randomize
for i in randperm(length(reg.staterefs))
slot = reg[i]
islocked(slot) || isassigned(slot) || return slot
end
end
for slot in reg
islocked(slot) || isassigned(slot) || return slot
end
Expand Down
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA
@doset "noninstant_and_backgrounds_qumode"
@doset "messagebuffer"
@doset "tags_and_queries"
@doset "entanglement_tracker"

@doset "circuitzoo_api"
@doset "circuitzoo_purification"
Expand Down
35 changes: 19 additions & 16 deletions test/test_entanglement_tracker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ using QuantumSavory.ProtocolZoo: EntanglementCounterpart, EntanglementHistory, E
using Graphs
using Test

using Logging
logger = ConsoleLogger(Logging.Debug; meta_formatter=(args...)->(:black,"",""))
global_logger(logger)
# using Logging
# logger = ConsoleLogger(Logging.Debug; meta_formatter=(args...)->(:black,"",""))
# global_logger(logger)

##

Expand Down Expand Up @@ -71,30 +71,33 @@ using Graphs
using Test
using Random

using Logging
logger = ConsoleLogger(Logging.Debug; meta_formatter=(args...)->(:black,"",""))
global_logger(logger)
# using Logging
# logger = ConsoleLogger(Logging.Debug; meta_formatter=(args...)->(:black,"",""))
# global_logger(logger)
# same but this time with an entanglement tracker
n = 5 # works at <5

for i in 1:1000
println("new =======================\n\n")
net = RegisterNet([Register(i+3) for i in 1:n])
n = rand(1:30)
net = RegisterNet([Register(j+3) for j in 1:n])
sim = get_time_tracker(net)
for i in vertices(net)
tracker = EntanglementTracker(sim, net, i)
for j in vertices(net)
tracker = EntanglementTracker(sim, net, j)
@process tracker()
end
for e in edges(net)
eprot = EntanglerProt(sim, net, e.src, e.dst; rounds=1)
eprot = EntanglerProt(sim, net, e.src, e.dst; rounds=1, randomize=true)
@process eprot()
end
for i in 2:n-1
swapper = SwapperProt(sim, net, i; rounds=1)
for j in 2:n-1
swapper = SwapperProt(sim, net, j; rounds=1)
@process swapper()
end
run(sim, 200)

@test net[1].tags[1] == [Tag(EntanglementCounterpart, n, 1)]
@test net[n].tags[1] == [Tag(EntanglementCounterpart, 1, 1)]
@test query(net[1], EntanglementCounterpart, n, ❓).tag[2] == n
@test query(net[n], EntanglementCounterpart, 1, ❓).tag[2] == 1

# @test net[1].tags[1] == [Tag(EntanglementCounterpart, n, 1)]
# @test net[n].tags[1] == [Tag(EntanglementCounterpart, 1, 1)]
#@test observable((net[1][1], net[n][1]), Z⊗Z) ≈ 1
end
4 changes: 2 additions & 2 deletions test/test_messagebuffer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ env = get_time_tracker(net);
while true
mb = messagebuffer(net, 2)
@yield wait(mb)
msg = querypop!(mb, :second_tag, ❓, ❓)
print("t=$(now(env)): query returns ")
msg = querydelete!(mb, :second_tag, ❓, ❓)
@yield timeout(env, 1.0)
if isnothing(msg)
#println("nothing")
else
Expand Down
Loading

0 comments on commit 1f58d09

Please sign in to comment.