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

grid entanglement with multiple paths #106

Merged
merged 12 commits into from
Mar 15, 2024
10 changes: 8 additions & 2 deletions src/ProtocolZoo/ProtocolZoo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ end

@resumable function (prot::EntanglerProt)()
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)
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..."
Krastanov marked this conversation as resolved.
Show resolved Hide resolved
@yield timeout(prot.sim, prot.retry_lock_time)
continue
end
Expand All @@ -185,9 +187,11 @@ end
# tag local node b with EntanglementCounterpart remote_node_idx_a remote_slot_idx_a
tag!(b, EntanglementCounterpart, prot.nodeA, a.idx)

@debug "EntanglerProt between $(prot.nodeA) and $(prot.nodeB)|round $(round): Entangled .$(a.idx) and .$(b.idx)"
unlock(a)
unlock(b)
rounds==-1 || (rounds -= 1)
round += 1
end
end

Expand Down Expand Up @@ -232,6 +236,7 @@ end

@resumable function (prot::SwapperProt)()
rounds = prot.rounds
round = 1
while rounds != 0
reg = prot.net[prot.node]
qubit_pair = findswapablequbits(prot.net, prot.node, prot.nodeL, prot.nodeH, prot.chooseL, prot.chooseH)
Expand Down Expand Up @@ -259,15 +264,16 @@ 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 "SwapperProt @$(prot.node): Send message to $(tag1[2]) | message=`$msg1`"
@debug "SwapperProt @$(prot.node)|round $(round): 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 "SwapperProt @$(prot.node): Send message to $(tag2[2]) | message=`$msg2`"
@debug "SwapperProt @$(prot.node)|round $(round): Send message to $(tag2[2]) | message=`$msg2`"
unlock(q1)
unlock(q2)
rounds==-1 || (rounds -= 1)
round += 1
end
end

Expand Down
57 changes: 39 additions & 18 deletions src/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ julia> queryall(r, :symbol, ❓, >(5))
@NamedTuple{slot::RegRef, tag::Tag}[]
```
"""
queryall(args...; kwargs...) = query(args..., Val{true}(); kwargs...)
queryall(args...; filo=true, kwargs...) = query(args..., Val{true}(); filo, kwargs...)


"""
Expand All @@ -85,6 +85,8 @@ Wildcards are supported (instances of `Wildcard` also available as the constants
Predicate functions are also supported (they have to be `Int`↦`Bool` functions).
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,
defaulting to `filo=true` (i.e. a stack-like behavior).

```jldoctest
julia> r = Register(10);
Expand Down Expand Up @@ -123,17 +125,28 @@ julia> query(r, Int, 4, <(7))

See also: [`queryall`](@ref), [`tag!`](@ref), [`W`](@ref), [`❓`](@ref)
"""
function query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing) where {allB}
find = allB ? findall : findfirst
i = find(i -> _nothingor(locked, islocked(reg[i])) && _nothingor(assigned, isassigned(reg[i])) && tag ∈ reg.tags[i],
1:length(reg))
if allB
return NamedTuple{(:slot, :tag), Tuple{RegRef, Tag}}[(slot=reg[i], tag=tag) for i in i]
else
isnothing(i) ? nothing : (;slot=reg[i], tag=tag)
end
function query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing, filo::Bool=true) where {allB}
_query(reg, tag, Val{allB}(), Val{filo}(); locked=locked, assigned=assigned)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this indirection from a Bool keyword argument to a Val{Bool} argument, is necessary if we want the compiler to guarantee that separate machine code is compiled for each of the two values (true / false).

It is also a way to avoid code duplication (e.g. queryall simply calls query(...,Val(true)))

It made sense when the function was simpler and it makes sense for private things.

But for public things like the filo keyword, it is cumbersome to use; Moreover, it is not simple anymore, so we should probably just split these methods, instead of having all these compile-time if/else statements (in Julia an if-else that depends only on information in the type system, will be decided during compilation and there will be no if/else actually happening when we run the program)

end

function _query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(), ::Val{filoB}=Val{true}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing) where {allB, filoB}
result = NamedTuple{(:slot, :depth, :tag), Tuple{RegRef, Int, Tag}}[]
for i in 1:length(reg)
if _nothingor(locked, islocked(reg[i])) && _nothingor(assigned, isassigned(reg[i]))
for res in _query(reg[i], tag, Val{true}(), Val{filoB}())
@show res
if allB
for r in res
push!(result, (slot=reg[i],r...))
end
else
return (slot=reg[i],res...)
end
end
end
end
return allB ? result : nothing
end


"""
Expand All @@ -157,10 +170,15 @@ 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}()) where {allB} # TODO there is a lot of code duplication here
find = allB ? findall : findfirst
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

function _query(ref::RegRef, tag::Tag, ::Val{allB}=Val{false}(), ::Val{filoB}=Val{true}()) where {allB, filoB} # TODO there is a lot of code duplication here
find = allB ? findall : filoB ? findlast : findfirst # findlast corresponds to filo because new tags are pushed at the end of the tags vector for a RegRef in `tag!`
i = find(==(tag), ref.reg.tags[ref.idx])
if allB
i = filoB ? reverse(i) : i # findall still starts looking from the first index, so we reverse here
return NamedTuple{(:depth, :tag), Tuple{Int, Tag}}[(depth=i, tag=tag) for i in i]
else
isnothing(i) ? nothing : (;depth=i, tag=tag)
Expand Down Expand Up @@ -309,15 +327,16 @@ for (tagsymbol, tagvariant) in pairs(tag_types)
argssig_wild = [:($a::$t) for (a,t) in zip(args, sig_wild)]
wild_checks = [:(isa($(args[i]),Wildcard) || $(args[i])(tag[$i])) for i in idx]
nonwild_checks = [:(tag[$i]==$(args[i])) for i in complement_idx]
newmethod_reg = quote function query(reg::Register, $(argssig_wild...), ::Val{allB}=Val{false}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing) where {allB}
res = NamedTuple{(:slot, :tag), Tuple{RegRef, Tag}}[]
newmethod_reg = quote function query(reg::Register, $(argssig_wild...), ::Val{allB}=Val{false}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing, filo::Bool=true) where {allB}
res = NamedTuple{(:slot, :depth, :tag), Tuple{RegRef, Int, Tag}}[]
for (reg_idx, tags) in enumerate(reg.tags)
slot = reg[reg_idx]
for tag in tags
for depth in (filo ? reverse(keys(tags)) : keys(tags))
tag = tags[depth]
if isvariant(tag, ($(tagsymbol,))[1]) # a weird workaround for interpolating a symbol as a symbol
(_nothingor(locked, islocked(slot)) && _nothingor(assigned, isassigned(slot))) || continue
if _all($(nonwild_checks...)) && _all($(wild_checks...))
allB ? push!(res, (;slot, tag)) : return (;slot, tag)
allB ? push!(res, (;slot, depth, tag)) : return (;slot, depth, tag)
end
end
end
Expand All @@ -333,9 +352,11 @@ for (tagsymbol, tagvariant) in pairs(tag_types)
end
end
end end
newmethod_rr = quote function query(ref::RegRef, $(argssig_wild...), ::Val{allB}=Val{false}()) where {allB}
newmethod_rr = quote function query(ref::RegRef, $(argssig_wild...), ::Val{allB}=Val{false}(); filo::Bool=true) where {allB}
res = NamedTuple{(:depth, :tag), Tuple{Int, Tag}}[]
for (depth, tag) in pairs(ref.reg.tags[ref.idx])
tags = ref.reg.tags[ref.idx]
for depth in (filo ? reverse(keys(tags)) : keys(tags))
tag = tags[depth]
if isvariant(tag, ($(tagsymbol,))[1]) # a weird workaround for interpolating a symbol as a symbol
if _all($(nonwild_checks...)) && _all($(wild_checks...))
allB ? push!(res, (;depth, tag)) : return (;depth, tag)
Expand Down
Loading
Loading