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

Store time of tagging #114

Merged
merged 36 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ad650ed
initial changes to the way tags are stored
ba2tripleO Apr 15, 2024
242024e
Revert "initial changes to the way tags are stored"
ba2tripleO Apr 17, 2024
dcbedeb
make the internals compatible with dictionary for query and tagging
ba2tripleO May 1, 2024
dedfb55
Merge branch 'QuantumSavory:master' into tag_time
ba2tripleO May 1, 2024
7266af1
Merge branch 'tag_time' of https://github.com/Abhishek-1Bhatt/Quantum…
ba2tripleO May 1, 2024
42342a3
fix typo
ba2tripleO May 1, 2024
1935828
Complete missing `querydelete!` tests
ba2tripleO May 2, 2024
c1aa85d
simplify tests
ba2tripleO May 2, 2024
1c49cd8
Fixes to doctests
ba2tripleO May 3, 2024
f847609
fix
ba2tripleO May 3, 2024
f82afbe
apply suggestions from code review
ba2tripleO May 4, 2024
b702261
update test_tags_and_queries.jl
ba2tripleO May 4, 2024
13c7a05
use `_querydelete` for both registers and refs
ba2tripleO May 8, 2024
b9481d6
Version bump for breaking release
ba2tripleO May 8, 2024
691911f
some more refinement on query interface
ba2tripleO May 8, 2024
6827364
update queries.jl
ba2tripleO May 8, 2024
0900da9
Update queries.jl
ba2tripleO May 8, 2024
f606f1b
Merge branch 'QuantumSavory:master' into tag_time
ba2tripleO May 15, 2024
0540e90
Apply suggestions from code review
ba2tripleO May 16, 2024
9930bac
remove misleading keyword argument from `querydelete!(::MessageBuffer)`
Krastanov May 20, 2024
c6f874e
remove internal implementation details from docstring
Krastanov May 20, 2024
064d8a5
fix a typo leading to a broken `querydelete!(::Register)` and add doc…
Krastanov May 20, 2024
5264da1
simplify querydelete! (needs updating tests)
Krastanov May 21, 2024
266f26a
clean up and simplify the first 45 lines of tests
Krastanov May 21, 2024
cfc02ad
clean up and simplify the tests up to line 80
Krastanov May 21, 2024
504aeb7
clean up and simplify the rest of the tests
Krastanov May 21, 2024
86c367e
fixup for a previous change
Krastanov May 21, 2024
d64515d
speed up test_entanglement_consumer (noticed it takes 5 minutes just …
Krastanov May 21, 2024
858002a
minor fixup in doctest runner
Krastanov May 21, 2024
f254e67
fixup
Krastanov May 21, 2024
92e87c9
remove internal implementation details from docstring
Krastanov May 21, 2024
f149b0c
minor simplification of _query for style consistency
Krastanov May 21, 2024
f0709ac
remove unused keyword argument from `tag!`
Krastanov May 21, 2024
6f5fc67
fixup
Krastanov May 21, 2024
48f6240
more filter fixes to docstrings
Krastanov May 21, 2024
2b3f537
fixup
Krastanov May 21, 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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QuantumSavory"
uuid = "2de2e421-972c-4cb5-a0c3-999c85908079"
authors = ["Stefan Krastanov <[email protected]>"]
version = "0.3.4"
version = "0.4.0"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Expand Down
20 changes: 10 additions & 10 deletions src/ProtocolZoo/ProtocolZoo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ end
margin = isentangled ? prot.margin : prot.hardmargin
a = findfreeslot(prot.net[prot.nodeA]; randomize=prot.randomize, margin=margin)
b = findfreeslot(prot.net[prot.nodeB]; randomize=prot.randomize, margin=margin)

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 @@ -253,16 +253,16 @@ end
continue
end

(q1, tag1) = qubit_pair[1].slot, qubit_pair[1].tag
(q2, tag2) = qubit_pair[2].slot, qubit_pair[2].tag
(q1, id1, tag1) = qubit_pair[1].slot, qubit_pair[1].id, qubit_pair[1].tag
(q2, id2, tag2) = qubit_pair[2].slot, qubit_pair[2].id, 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)
untag!(q1, id1)
# 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)

untag!(q2, tag2)
untag!(q2, id2)
# 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!(q2, EntanglementHistory, tag2[2], tag2[3], tag1[2], tag1[3], q1.idx)

Expand Down Expand Up @@ -362,7 +362,7 @@ end
❓) # which local slot used to be entangled with whom we swapped with
if !isnothing(history)
# @debug "tracker @$(prot.node) history: $(history) | msg: $msg"
_, _, _, whoweswappedwith_node, whoweswappedwith_slotidx, swappedlocal_slotidx = history
_, _, _, whoweswappedwith_node, whoweswappedwith_slotidx, swappedlocal_slotidx = history.tag
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)
Expand Down Expand Up @@ -423,14 +423,14 @@ end
continue
end
end
q1 = query1.slot

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

@debug "EntanglementConsumer between $(prot.nodeA) and $(prot.nodeB): queries successful, consuming entanglement"
untag!(q1, query1.tag)
untag!(q2, query2.tag)
untag!(q1, query1.id)
untag!(q2, query2.id)
# 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 = real(observable((q1, q2), Z⊗Z))
Expand Down
6 changes: 6 additions & 0 deletions src/QuantumSavory.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
module QuantumSavory

const glcnt = Ref{Int128}(0)

function guid()
glcnt[] += 1
end

using Reexport

using DocStringExtensions
Expand Down
141 changes: 73 additions & 68 deletions src/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,27 @@

Assign a tag to a slot in a register.

It returns the list of all currently present tags for that register.

See also: [`query`](@ref), [`untag!`](@ref)"""
function tag!(ref::RegRef, tag::Tag)
push!(ref.reg.tags[ref.idx], tag)
id = guid()
push!(ref.reg.guids, id)
ref.reg.tag_info[id] = (tag, ref.idx, now(get_time_tracker(ref)))
end

tag!(ref, tag) = tag!(ref, Tag(tag))
tag!(ref, tag) = tag!(ref,Tag(tag))


"""$TYPEDSIGNATURES

Removes the first instance of tag from the list to tags associated with a [`RegRef`](@ref) in a [`Register`](@ref)

It returns the list of all currently present tags for that register.

See also: [`query`](@ref), [`tag!`](@ref)
"""
function untag!(ref::RegRef, tag::Tag) # TODO rather slow implementation. See issue #74
tags = ref.reg.tags[ref.idx]
i = findfirst(==(tag), tags)
isnothing(i) ? throw(KeyError(tag)) : deleteat!(tags, i) # TODO make sure there is a clear error message
function untag!(ref::RegRef, id::Int128)
i = findfirst(==(id), ref.reg.guids)
isnothing(i) ? throw(KeyError(tag)) : deleteat!(ref.reg.guids, i) # TODO make sure there is a clear error message
delete!(ref.reg.tag_info, id)
nothing
end


Expand Down Expand Up @@ -55,22 +54,22 @@ $TYPEDSIGNATURES

A query function that returns all slots of a register that have a given tag, with support for predicates and wildcards.

```jldoctest
```jldoctest; filter = r"id = (\\d*), "
julia> r = Register(10);
tag!(r[1], :symbol, 2, 3);
tag!(r[2], :symbol, 4, 5);

julia> queryall(r, :symbol, ❓, ❓)
2-element Vector{@NamedTuple{slot::RegRef, depth::Int64, tag::Tag}}:
(slot = Slot 1, depth = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
(slot = Slot 2, depth = 1, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
2-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 2, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
(slot = Slot 1, id = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)

julia> queryall(r, :symbol, ❓, >(4))
1-element Vector{@NamedTuple{slot::RegRef, depth::Int64, tag::Tag}}:
(slot = Slot 2, depth = 1, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 2, tag = SymbolIntInt(:symbol, 4, 5)::Tag)

julia> queryall(r, :symbol, ❓, >(5))
@NamedTuple{slot::RegRef, depth::Int64, tag::Tag}[]
@NamedTuple{slot::RegRef, id::Int128, tag::Tag}[]
```
"""
queryall(args...; filo=true, kwargs...) = query(args..., Val{true}(); filo, kwargs...)
Expand All @@ -88,22 +87,22 @@ 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
```jldoctest; filter = r"id = (\\d*), "
julia> r = Register(10);
tag!(r[1], :symbol, 2, 3);
tag!(r[2], :symbol, 4, 5);


julia> query(r, :symbol, 4, 5)
(slot = Slot 2, depth = 1, tag = SymbolIntInt(:symbol, 4, 5)::Tag)
(slot = Slot 2, id = 4, tag = SymbolIntInt(:symbol, 4, 5)::Tag)

julia> lock(r[1]);

julia> query(r, :symbol, 4, 5; locked=false) |> isnothing
false

julia> query(r, :symbol, ❓, 3)
(slot = Slot 1, depth = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
(slot = Slot 1, id = 3, tag = SymbolIntInt(:symbol, 2, 3)::Tag)

julia> query(r, :symbol, ❓, 3; assigned=true) |> isnothing
true
Expand All @@ -120,7 +119,7 @@ julia> query(r, Int, 4, >(7)) |> isnothing
true

julia> query(r, Int, 4, <(7))
(slot = Slot 5, depth = 1, tag = TypeIntInt(Int64, 4, 5)::Tag)
(slot = Slot 5, id = 5, tag = TypeIntInt(Int64, 4, 5)::Tag)
```

See also: [`queryall`](@ref), [`tag!`](@ref), [`W`](@ref), [`❓`](@ref)
Expand All @@ -129,12 +128,14 @@ function query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(); locked::Union{
_query(reg, tag, Val{allB}(), Val{filo}(); locked=locked, assigned=assigned)
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}())
allB ? push!(result, (slot=reg[i],res...)) : return (slot=reg[i],res...)
function _query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(), ::Val{filoB}=Val{true}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing, ref=nothing) where {allB, filoB}
result = NamedTuple{(:slot, :id, :tag), Tuple{RegRef, Int128, Tag}}[]
op_guid = filoB ? reverse : identity
for i in op_guid(reg.guids)
slot = reg[reg.tag_info[i][2]]
if reg.tag_info[i][1] == tag && _nothingor(ref, slot) # Need to check slot when calling from `query` dispatch on RegRef
if _nothingor(locked, islocked(slot) && _nothingor(assigned, isassigned(slot)))
allB ? push!(result, (slot=slot, id=i, tag=reg.tag_info[i][1])) : return (slot=slot, id=i, tag=reg.tag_info[i][1])
end
end
end
Expand All @@ -147,36 +148,24 @@ $TYPEDSIGNATURES

A [`query`](@ref) on a single slot of a register.

```jldoctest
```jldoctest; filter = r"id = (\\d*), "
julia> r = Register(5);

julia> tag!(r[2], :symbol, 2, 3);

julia> query(r[2], :symbol, 2, 3)
(depth = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
(slot = Slot 2, id = 6, tag = SymbolIntInt(:symbol, 2, 3)::Tag)

julia> query(r[3], :symbol, 2, 3) === nothing
true

julia> queryall(r[2], :symbol, 2, 3)
1-element Vector{@NamedTuple{depth::Int64, tag::Tag}}:
(depth = 1, tag = SymbolIntInt(:symbol, 2, 3)::Tag)
1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 6, 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

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
if allB
i = findall(==(tag), ref.reg.tags[ref.idx])
i = filoB ? reverse(i) : i
return NamedTuple{(:depth, :tag), Tuple{Int, Tag}}[(depth=i, tag=tag) for i in i]
else
find = filoB ? findlast : findfirst
i = find(==(tag), ref.reg.tags[ref.idx])
return isnothing(i) ? nothing : (;depth=i, tag=tag)
end
function query(ref::RegRef, tag::Tag, ::Val{allB}=Val{false}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing, filo::Bool=true) where {allB}
_query(ref.reg, tag, Val{allB}(), Val{filo}(); locked=locked, assigned=assigned, ref=ref)
end


Expand Down Expand Up @@ -272,17 +261,34 @@ function querydelete!(mb::MessageBuffer, args...)
return isnothing(r) ? nothing : popat!(mb.buffer, r.depth)
end



"""
$TYPEDSIGNATURES

A [`query`](@ref) for [`RegRef`](@ref) that also deletes the tag from the tag list for the `RegRef`.
Allows the user to specify order of accessing tags to be FILO or FIFO.
A [`query`](@ref) for [`Register`](@ref) or a register slot (i.e. a [`RegRef`](@ref)) that also deletes the tag.

```jldoctest; filter = r"id = (\\d*), "
julia> reg = Register(3)
tag!(reg[1], :tagA, 1, 2, 3)
tag!(reg[2], :tagA, 10, 20, 30)
tag!(reg[2], :tagB, 6, 7, 8);

julia> queryall(reg, :tagA, ❓, ❓, ❓)
2-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 2, id = 4, tag = SymbolIntIntInt(:tagA, 10, 20, 30)::Tag)
(slot = Slot 1, id = 3, tag = SymbolIntIntInt(:tagA, 1, 2, 3)::Tag)

julia> querydelete!(reg, :tagA, ❓, ❓, ❓)
(slot = Slot 2, id = 4, tag = SymbolIntIntInt(:tagA, 10, 20, 30)::Tag)

julia> queryall(reg, :tagA, ❓, ❓, ❓)
1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag}}:
(slot = Slot 1, id = 3, tag = SymbolIntIntInt(:tagA, 1, 2, 3)::Tag)
```
"""
function querydelete!(ref::RegRef, args...; filo::Bool=true) # TODO there is a lot of code duplication here
r = query(ref, args...; filo)
return isnothing(r) ? nothing : popat!(ref.reg.tags[ref.idx], r.depth)
function querydelete!(reg::Union{Register,RegRef}, args...; kwa...)
r = query(reg, args..., Val{false}(); kwa...)
isnothing(r) || untag!(r.slot, r.id)
return r
end


Expand Down Expand Up @@ -323,16 +329,15 @@ for (tagsymbol, tagvariant) in pairs(tag_types)
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, 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 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, depth, tag)) : return (;slot, depth, tag)
end
res = NamedTuple{(:slot, :id, :tag), Tuple{RegRef, Int128, Tag}}[]
op_guid = filo ? reverse : identity
for i in op_guid(reg.guids)
tag = reg.tag_info[i][1]
slot = reg[reg.tag_info[i][2]]
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=slot, id=i, tag=tag)) : return (slot=slot, id=i, tag=tag)
end
end
end
Expand All @@ -348,13 +353,13 @@ for (tagsymbol, tagvariant) in pairs(tag_types)
end
end end
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}}[]
tags = ref.reg.tags[ref.idx]
for depth in (filo ? reverse(keys(tags)) : keys(tags))
tag = tags[depth]
res = NamedTuple{(:slot, :id, :tag), Tuple{RegRef, Int128, Tag}}[]
op_guid = filo ? reverse : identity
for i in op_guid(ref.reg.guids)
tag = ref.reg.tag_info[i][1]
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)
if _all($(nonwild_checks...)) && _all($(wild_checks...)) && (ref.reg[ref.reg.tag_info[i][2]] == ref)
allB ? push!(res, (slot=ref, id=i, tag=tag)) : return (slot=ref, id=i, tag=tag)
end
end
end
Expand Down
5 changes: 3 additions & 2 deletions src/states_registers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ struct Register # TODO better type description
stateindices::Vector{Int}
accesstimes::Vector{Float64} # TODO do not hardcode the type
locks::Vector{Any}
tags::Vector{Vector{Tag}} # TODO this is a rather inefficient way to store tags, but at least it provides a FIFO ordering; see issue #74
tag_info::Dict{Int128, Tuple{Tag, Int64, Union{Float64, Nothing}}}
guids::Vector{Int128}
end

function Register(traits, reprs, bg, sr, si, at)
env = ConcurrentSim.Simulation()
Register(traits, reprs, bg, sr, si, at, [ConcurrentSim.Resource(env) for _ in traits], [Vector{Tag}() for _ in traits])
Register(traits, reprs, bg, sr, si, at, [ConcurrentSim.Resource(env) for _ in traits], Dict{Int128, Tuple{Tag, Int64, Float64}}(), [])
end
Register(traits,reprs,bg,sr,si) = Register(traits,reprs,bg,sr,si,zeros(length(traits)))
Register(traits,reprs,bg) = Register(traits,reprs,bg,fill(nothing,length(traits)),zeros(Int,length(traits)),zeros(length(traits)))
Expand Down
2 changes: 1 addition & 1 deletion test/test_doctests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ using QuantumSavory

function doctests()
@testset "Doctests" begin
DocMeta.setdocmeta!(QuantumSavory, :DocTestSetup, :(using QuantumSavory; using QuantumSavory.CircuitZoo; using Graphs); recursive=true)
DocMeta.setdocmeta!(QuantumSavory, :DocTestSetup, :(using QuantumSavory; using QuantumSavory.CircuitZoo; using QuantumSavory.ProtocolZoo; using QuantumSavory.StatesZoo; using Graphs); recursive=true)
doctest(QuantumSavory;
#fix=true
)
Expand Down
2 changes: 1 addition & 1 deletion test/test_entanglement_consumer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if isinteractive()
end


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

Expand Down
Loading
Loading