Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EntanglementConsumer #86

Merged
merged 29 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
19a3a9a
Initial EntanglementConsumer setup with a very loose test around the …
ba2tro Jan 4, 2024
722311d
upper bound for compat
ba2tro Jan 5, 2024
0d593b5
traceout
ba2tro Jan 5, 2024
65f943d
add fifo and filo order for queries and some code rearrangement
ba2tro Jan 15, 2024
0a0f99b
fix for query test
ba2tro Jan 15, 2024
4ac7bfc
Merge branch 'master' into entanglementconsumer
ba2tro Feb 1, 2024
8ebeecb
update and polish the code
ba2tro Feb 2, 2024
2fd1da5
update test_entanglement_consumer.jl
ba2tro Feb 2, 2024
680a023
Update test_entanglement_consumer.jl
ba2tro Feb 2, 2024
8e1f3cd
Add margins to EntanglerProt
ba2tro Feb 5, 2024
4deca04
Merge branch 'master' into entanglementconsumer
ba2tro Feb 13, 2024
463f4f6
Merge branch 'entanglementconsumer' of https://github.com/Abhishek-1B…
ba2tro Feb 13, 2024
7971807
update the code with new changes in swapper, remove typos
ba2tro Feb 14, 2024
a1df3a1
test thoroughly
ba2tro Feb 14, 2024
b6163ff
update ProtocolZoo.jl
ba2tro Feb 23, 2024
b99cd4b
Update test_entanglement_consumer.jl
ba2tro Feb 28, 2024
a320db7
Merge branch 'QuantumSavory:master' into entanglementconsumer
ba2tro Feb 28, 2024
e24b574
Update test_entanglement_consumer.jl
ba2tro Mar 1, 2024
87e3c95
Merge branch 'master' into entanglementconsumer
ba2tro Mar 27, 2024
aacea30
call traceout once, make queries with kwargs
ba2tro Mar 27, 2024
5c2ce5d
modify the margin functionality
ba2tro Mar 29, 2024
f50de9c
change docstrings
ba2tro Mar 29, 2024
a8e989c
apply suggestions from review
ba2tro Mar 31, 2024
ef7c7c6
Update test_entanglement_consumer.jl
ba2tro Mar 31, 2024
6e0130e
Fixes to docs
ba2tro Apr 3, 2024
187348e
minor rewording of the docs to make the bullet points shorter (and so…
Krastanov Apr 4, 2024
8593f38
do not require the user to make empty arrays
Krastanov Apr 4, 2024
d455681
add missing constructor
Krastanov Apr 4, 2024
461ac46
don't log when queries fail
ba2tro Apr 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/CircuitZoo/CircuitZoo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ function inputqubits end
struct EntanglementSwap <: AbstractCircuit
end

function (::EntanglementSwap)(localL, remoteL, lacalR, remoteR)
apply!((localL, lacalR), CNOT)
function (::EntanglementSwap)(localL, remoteL, localR, remoteR)
apply!((localL, localR), CNOT)
xmeas = project_traceout!(localL, σˣ)
zmeas = project_traceout!(lacalR, σᶻ)
zmeas = project_traceout!(localR, σᶻ)
if xmeas==2
apply!(remoteL, Z)
end
Expand All @@ -37,10 +37,10 @@ inputqubits(::EntanglementSwap) = 4
struct LocalEntanglementSwap <: AbstractCircuit
end

function (::LocalEntanglementSwap)(localL, lacalR)
apply!((localL, lacalR), CNOT)
function (::LocalEntanglementSwap)(localL, localR)
apply!((localL, localR), CNOT)
xmeas = project_traceout!(localL, σˣ)
zmeas = project_traceout!(lacalR, σᶻ)
zmeas = project_traceout!(localR, σᶻ)
xmeas, zmeas
end

Expand Down
85 changes: 82 additions & 3 deletions src/ProtocolZoo/ProtocolZoo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
using ResumableFunctions: @resumable
import SumTypes

export EntanglerProt, SwapperProt, EntanglementTracker
export EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer

abstract type AbstractProtocol end

Expand Down Expand Up @@ -149,6 +149,10 @@
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
"""when we have the protocol set to run for infinite rounds, it may lead to a situation where all the slots of a node are entangled to node(s) on just one side in which case, no swaps can occur. Hence, this field can be used to always leave a chosen number of slots out of all the available slots free for entanglement generation at the next edge, if there exists previous entanglement at the current edge"""
margin::Int = 0
"""when we have the protocol set to run for infinite rounds, it may lead to a situation where all the slots of a node are entangled to node(s) on just one side in which case, no swaps can occur. Hence, this field can be used to always leave a chosen number of slots out of all the available slots free for entanglement generation at the next edge, if there's no previous entanglement at the current edge"""
hardmargin::Int = 0
end

"""Convenience constructor for specifying `rate` of generation instead of success probability and time"""
Expand All @@ -166,8 +170,19 @@
rounds = prot.rounds
round = 1
while rounds != 0
a = findfreeslot(prot.net[prot.nodeA], randomize=prot.randomize)
b = findfreeslot(prot.net[prot.nodeB], randomize=prot.randomize)
freeA = sum([!isassigned(prot.net[prot.nodeA][i]) for i in 1:length(prot.net[prot.nodeA].staterefs)])
freeB = sum([!isassigned(prot.net[prot.nodeB][i]) for i in 1:length(prot.net[prot.nodeB].staterefs)])
isentangled = !isnothing(query(prot.net[prot.nodeA], EntanglementCounterpart, prot.nodeB, ❓;assigned=true))
margin = isentangled ? prot.margin : prot.hardmargin
if freeA >= prot.margin && freeB >= prot.margin
a = findfreeslot(prot.net[prot.nodeA]; randomize=prot.randomize)
b = findfreeslot(prot.net[prot.nodeB]; randomize=prot.randomize)
ba2tro marked this conversation as resolved.
Show resolved Hide resolved
else
@debug "EntanglerProt between $(prot.nodeA) and $(prot.nodeB)|Skipping current iteration due to lack of free slots at one or both of the nodes"
@yield timeout(prot.sim, prot.retry_lock_time)

Check warning on line 182 in src/ProtocolZoo/ProtocolZoo.jl

View check run for this annotation

Codecov / codecov/patch

src/ProtocolZoo/ProtocolZoo.jl#L182

Added line #L182 was not covered by tests
continue
end

if isnothing(a) || isnothing(b)
isnothing(prot.retry_lock_time) && error("We do not yet support waiting on register to make qubits available") # TODO
@debug "EntanglerProt between $(prot.nodeA) and $(prot.nodeB)|round $(round): Failed to find free slots. \n Got:\n \t $a \n \t $b \n retrying..."
Expand Down Expand Up @@ -245,10 +260,12 @@
@yield timeout(prot.sim, prot.retry_lock_time)
continue
end

(q1, tag1) = qubit_pair[1].slot, qubit_pair[1].tag
(q2, tag2) = qubit_pair[2].slot, qubit_pair[2].tag
@yield lock(q1) & lock(q2) # this should not really need a yield thanks to `findswapablequbits`, but it is better to be defensive
@yield timeout(prot.sim, prot.local_busy_time)

untag!(q1, tag1)
# store a history of whom we were entangled to: remote_node_idx, remote_slot_idx, remote_swapnode_idx, remote_swapslot_idx, local_swap_idx
tag!(q1, EntanglementHistory, tag1[2], tag1[3], tag2[2], tag2[3], q2.idx)
Expand Down Expand Up @@ -369,4 +386,66 @@
end
end

"""
$TYPEDEF

A protocol running between two nodes, checking periodically for any entangled pairs between the two nodes and consuming/emptying the qubit slots.

$FIELDS
"""
@kwdef struct EntanglementConsumer <:AbstractProtocol
"""time-and-schedule-tracking instance from `ConcurrentSim`"""
sim::Simulation
"""a network graph of registers"""
net::RegisterNet
"""the vertex index of node A"""
nodeA::Int
"""the vertex index of node B"""
nodeB::Int
"""stores the time and resulting observable from querying nodeA and nodeB for `EntanglementCounterpart`"""
log::Vector{Any}
"""Time period between successive queries on the nodes"""
period::Float64
end

@resumable function (prot::EntanglementConsumer)(;_prot::EntanglementConsumer=prot)
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
while true
query1 = query(prot.net[prot.nodeA], EntanglementCounterpart, prot.nodeB, ❓;locked=false, assigned=true) # TODO Need a `querydelete!` dispatch on `Register` rather than using `query` here followed by `untag!` below
if isnothing(query1)
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query1 failed"
push!(prot.log, (now(prot.sim), nothing, nothing))
@yield timeout(prot.sim, prot.period)

Check warning on line 418 in src/ProtocolZoo/ProtocolZoo.jl

View check run for this annotation

Codecov / codecov/patch

src/ProtocolZoo/ProtocolZoo.jl#L418

Added line #L418 was not covered by tests
continue
else
query2 = query(prot.net[prot.nodeB], EntanglementCounterpart, prot.nodeA, query1.slot.idx;locked=false, assigned=true)

if isnothing(query2) # in case EntanglementUpdate hasn't reached the second node yet, but the first node has the EntanglementCounterpart
@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): query2 failed"
push!(prot.log, (now(prot.sim), nothing, nothing))
@yield timeout(prot.sim, prot.period)
continue

Check warning on line 427 in src/ProtocolZoo/ProtocolZoo.jl

View check run for this annotation

Codecov / codecov/patch

src/ProtocolZoo/ProtocolZoo.jl#L424-L427

Added lines #L424 - L427 were not covered by tests
end
end

q1 = query1.slot
q2 = query2.slot
@yield lock(q1) & lock(q2)

Check warning on line 433 in src/ProtocolZoo/ProtocolZoo.jl

View check run for this annotation

Codecov / codecov/patch

src/ProtocolZoo/ProtocolZoo.jl#L433

Added line #L433 was not covered by tests

@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): queries successful, consuming entanglement"
untag!(q1, query1.tag)
untag!(q2, query2.tag)
# TODO do we need to add EntanglementHistory and should that be a different EntanglementHistory since the current one is specifically for SwapperProt
# TODO currently when calculating the observable we assume that EntanglerProt.pairstate is always (|00⟩ + |11⟩)/√2, make it more general for other states
ob1 = observable((q1, q2), Z⊗Z)
ob2 = observable((q1, q2), X⊗X)

traceout!(prot.net[prot.nodeA][q1.idx], prot.net[prot.nodeB][q2.idx])
push!(prot.log, (now(prot.sim), ob1, ob2))
unlock(q1)
unlock(q2)
@yield timeout(prot.sim, prot.period)
end

Check warning on line 448 in src/ProtocolZoo/ProtocolZoo.jl

View check run for this annotation

Codecov / codecov/patch

src/ProtocolZoo/ProtocolZoo.jl#L447-L448

Added lines #L447 - L448 were not covered by tests
end

end # module
5 changes: 3 additions & 2 deletions src/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ julia> queryall(r, :symbol, ❓, >(5))
"""
queryall(args...; filo=true, kwargs...) = query(args..., Val{true}(); filo, kwargs...)


"""
$TYPEDSIGNATURES

A query function searching for the first slot in a register that has a given tag.

Wildcards are supported (instances of `Wildcard` also available as the constants [`W`](@ref) or the emoji [`❓`](@ref) which can be entered as `\\:question:` in the REPL).
Predicate functions are also supported (they have to be `Int`↦`Bool` functions).
The order of query lookup can be specified in terms of FIFO or FILO and defaults to FIFO if not specified.
ba2tro marked this conversation as resolved.
Show resolved Hide resolved
The keyword arguments `locked` and `assigned` can be used to check, respectively,
whether the given slot is locked or whether it contains a quantum state.
The keyword argument `filo` can be used to specify whether the search should be done in a FIFO or FILO order,
Expand Down Expand Up @@ -163,6 +163,7 @@ julia> queryall(r[2], :symbol, 2, 3)
(depth = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
```
"""

function query(ref::RegRef, tag::Tag, ::Val{allB}=Val{false}(); filo::Bool=true) where {allB} # TODO this should support locked and assigned like query(::Register)
_query(ref, tag, Val{allB}(), Val{filo}())
end
Expand Down Expand Up @@ -398,6 +399,6 @@ end


function Base.isassigned(r::Register,i::Int) # TODO erase
r.stateindices[i] != 0 # TODO this also usually means r.staterenfs[i] !== nothing - choose one and make things consistent
r.stateindices[i] != 0 # TODO this also usually means r.staterefs[i] !== nothing - choose one and make things consistent
end
Base.isassigned(r::RegRef) = isassigned(r.reg, r.idx)
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ println("Starting tests with $(Threads.nthreads()) threads out of `Sys.CPU_THREA
@doset "messagebuffer"
@doset "tags_and_queries"
@doset "entanglement_tracker"
@doset "entanglement_consumer"
@doset "entanglement_tracker_grid"


@doset "circuitzoo_api"
@doset "circuitzoo_ent_swap"
@doset "circuitzoo_purification"
Expand Down
47 changes: 47 additions & 0 deletions test/test_entanglement_consumer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using QuantumSavory
using QuantumSavory.ProtocolZoo: EntanglerProt, SwapperProt, EntanglementTracker, EntanglementConsumer
using Graphs
using ConcurrentSim
using Test

if isinteractive()
using Logging
logger = ConsoleLogger(Logging.Warn; meta_formatter=(args...)->(:black,"",""))
global_logger(logger)
println("Logger set to debug")
end


for i in 1:30, n in 3:30
net = RegisterNet([Register(10) for j in 1:n])
sim = get_time_tracker(net)

for e in edges(net)
eprot = EntanglerProt(sim, net, e.src, e.dst; rounds=-1, randomize=true, margin=5, hardmargin=3)
@process eprot()
end

for v in 2:n-1
sprot = SwapperProt(sim, net, v; nodeL = <(v), nodeH = >(v), chooseL = argmin, chooseH = argmax, rounds = -1)
@process sprot()
end

for v in vertices(net)
etracker = EntanglementTracker(sim, net, v)
@process etracker()
end

econ = EntanglementConsumer(sim, net, 1, n, [], 1.0)
@process econ()

run(sim, 100)


for i in 1:length(econ.log)
if !isnothing(econ.log[i][2])
@test econ.log[i][2] ≈ 1.0
@test econ.log[i][3] ≈ 1.0
end
end

end
1 change: 1 addition & 0 deletions test/test_tags_and_queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ tag!(r[5], Int, 4, 5)
@test querydelete!(r[2], :symbol1, 4, ❓) === nothing



# tests for fifo and filo order queries (default is filo)
# for RegRefs

Expand Down
Loading