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

added Query Interface and some doc fixes #92

Merged
merged 16 commits into from
Mar 5, 2024
Merged
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function main()
"Visualizations" => "visualizations.md",
"Dev Documentation" => [
"Register Interface" => "register_interface.md",
"Query Interface" => "query_interface.md",
],
],
"How-To Guides" => [
Expand Down
120 changes: 120 additions & 0 deletions docs/src/query_interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Query Interface

```@meta
DocTestSetup = quote
using QuantumSavory
end
```
ba2tro marked this conversation as resolved.
Show resolved Hide resolved


## `Tag`
Tags are identifiers which are used to represent classical information needed for a quantum information. The library allows the construction of custom tags following the format of one of the [`tag_types`](@ref) using the `Tag` constructor. The library implements the following tags for use in the networking protocols:
ba2tro marked this conversation as resolved.
Show resolved Hide resolved

- `EntanglementCounterpart`
-`remote_node`
-`remote_slot`
It indicates the current entanglement status with a remote node's slot.
ba2tro marked this conversation as resolved.
Show resolved Hide resolved

- `EntanglementHistory`
- `remote_node`
- `remote_slot`
- `swap_remote_node`
- `swap_remote_slot`
- `swapped_local`
This tag is used to store the outdated entanglement information after a swap. It helps to direct incoming entanglement update messages to the right node after a swap.
ba2tro marked this conversation as resolved.
Show resolved Hide resolved

- `EntanglementUpdateX`
- `past_local_node`
- `past_local_slot`
- `past_remote_slot`
- `new_remote_node`
- `new_remote_slot`
- `correction`
This tag arrives as a message from a remote node to which the current node was entangled to updat the entanglement information and apply an `X` correction after the remote node performs an entanglement swap.

- `EntanglementUpdateZ`
- `past_local_node`
- `past_local_slot`
- `past_remote_slot`
- `new_remote_node`
- `new_remote_slot`
- `correction`
This tag arrives as a message from a remote node to which the current node was entangled to updat the entanglement information and apply a `Z` correction after the remote node performs an entanglement swap.
ba2tro marked this conversation as resolved.
Show resolved Hide resolved

The tags are constructed using the `Tag` constructor
#### Tag(tagsymbol::Symbol, tagvariants...)
Copy link
Member

Choose a reason for hiding this comment

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

This is only one way to construct a tag, and in particular the tags described above are NOT constructed this way.

Copy link
Member Author

Choose a reason for hiding this comment

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

I couldn't get that, isn't this the only way we create tags. Even in ProtocolZoo, we never explicity create instances of the tags described above, its always done through tag! which is listed below this part. Is there something I'm missing here?

Copy link
Member Author

@ba2tro ba2tro Jan 29, 2024

Choose a reason for hiding this comment

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

Oh, did you mean doing something like,

tag_types.SymbolIntInt(:symbol1, 1, 3)

I'll list this way too here, let me know if you meant something else

But this still won't allow to define the above tags

using QuantumSavory
using QuantumSavory.ProtocolZoo: EntanglementCounterpart

mytag = QuantumSavory.tag_types.SymbolIntInt(EntanglementCounterpart, 1, 3)
ERROR: MethodError: Cannot `convert` an object of type Type{EntanglementCounterpart} to an object of type Symbol

To create the above tags only Tag seems to be available and tag! is used when we add the tag to a ref

where `tagvariants` are the extra arguments required by the specific `tagsymbol`, for instance the `tag_types.SymbolIntInt` require two `Int` values. It supports the use of predicate functions (`Int -> Bool`) and [`Wildcard`](@ref) (❓) in place of the `tagvariants` which allows the user to perform queries for tags fulfilling certain criteria.

## `tag!`
Adds a `Tag` to the list of tags associated with a [`RegRef`](@ref) in a [`Register`](@ref)
#### `tag!(ref::RegRef, tag::Tag)`
ba2tro marked this conversation as resolved.
Show resolved Hide resolved

## `untag!`
Removes the first matching tag from the list to tags associated with a [`RegRef`](@ref) in a [`Register`](@ref)
#### `untag!(ref::RegRef, tag::Tag)`

## [`query`](@ref)

[`query`](@ref) methods allow the user to query for `Tag`(s) in three different cases:
- on a particular qubit slot([`RegRef`](@ref)) in a [`Register`](@ref) node;
- on a [`Register`](@ref) to query for a slot that contains the passed `Tag`; and
- on a `MessageBuffer` to query for a particular `Tag` received from another node in a network.

The following features are supported:
- The query methods specialized on [`RegRef`](@ref) and [`Register`](@ref) allow for the queries to be executed in `FIFO` or `FILO` order, which is set to be `FIFO` by default. This means by default, a query on a [`RegRef`](@ref) returns the `Tag` which is at the end of the vector of tags attribute in a [`Register`](@ref), as new tags are pushed to the back by [`tag!`](@ref). On a [`Register`](@ref) it returns the slot number with the highest index having the queried `Tag`.

- The `Tag` passed to the method can be constructed using predicate functions (of the form: `Int` -> `Bool`) and [`Wildcard`](@ref) (❓). This supports querying for tags for which all the information is not known or isn't relevant, e.g, when looking for a qubit entangled with a neighbouring node in a repeater chain, we need a node that has a larger(right) or smaller(left) node number and the slot number of the neighbouring node to which its entangled is irrelevant. Hence, the `EntanglementCounterpart` tag passed to the [`query`](@ref) has a predicate `>(node)` or `<(node)` for `remote_node` and a [`Wildcard`](@ref) (❓) for `remote_slot` fields of the tag.

- It can be specified that the target slot be locked(or unlocked) and assigned(or unassigned) using the `locked` and `assigned` keywords which take `Bool` values. By default, the [`query`](@ref) does not check for these properties. This is available for [`query`](@ref) methods defined on [`Register`](@ref) and [`RegRef`](@ref).

#### `query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(), ::Val{fifo}=Val{true}(); locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing)`

#### `query(ref::RegRef, tag::Tag, ::Val{allB}=Val{false}(), ::Val{fifo}=Val{true}())`

#### `query(mb::MessageBuffer, tag::Tag)`

## `querydelete!`
A method on top of [`query`](@ref) which allows to query for tag in a [`RegRef`](@ref) and `MessageBuffer` returning the tag that satisfies the passed predicates and [`Wildcard`](@ref)s and deleting it from the list at the same time. It allows the same arguments to be passed to it as the corresponding [`query`](@ref) method on the data structure its called upon.

#### `querydelete!(ref::RegRef, args...)`

#### Interface Overview

```@raw html
<div class="mermaid">
flowchart TB
A["<code>querydelete!(ref::RegRef, args...)</code>"]
B["<code>query(ref::RegRef, tag::Tag, ::Val{allB}=Val{false}(), ::Val{fifo}=Val{true}())</code>"]
A --> B
</div>
```

#### `querydelete!(mb::MessageBuffer, args...)`

#### Interface Overview

```@raw html
<div class="mermaid">
flowchart TB
A["<code>querydelete!(mb::MessageBuffer, args...)</code>"]
B["<code>query(mb::MessageBuffer, tag::Tag)</code>"]
A --> B
</div>
```

## `queryall`
A method defined on top of [`query`](@ref) which allows to query for all tags in a [`RegRef`](@ref) or a [`Register`](@ref) that match the passed `Tag`, instead of just one matching instance.

#### `queryall(args...; kwargs...)`

where `args...` and `kwargs...` correspond to the arguments and keyword arguments accepted by the [`query`](@ref) method on the particular data structure on which the method is called upon.

#### Interface Overview

```@raw html
<div class="mermaid">
flowchart TB
A["<code>queryall(args...; kwargs...)</code>"]
B["<code>query(args..., ::Val{allB}=Val{true}(), ::Val{fifo}=Val{true}; kwargs...)</code>"]
</div>
```
18 changes: 9 additions & 9 deletions src/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function query(mb::MessageBuffer, tag::Tag)
return isnothing(i) ? nothing : (;depth=i, src=mb.buffer[i][1], tag=mb.buffer[i][2])
end

raw"""A [`query`](@ref) for classical message buffers that also pops the message out of the buffer.
raw"""A [`query`](@ref) for classical message buffers that also deletes the message out of the buffer.

```jldoctest
julia> net = RegisterNet([Register(3), Register(2)])
Expand All @@ -159,22 +159,22 @@ julia> run(get_time_tracker(net))
julia> query(messagebuffer(net, 2), :my_tag)
(depth = 1, src = 1, tag = Symbol(:my_tag)::Tag)

julia> querypop!(messagebuffer(net, 2), :my_tag)
julia> querydelete!(messagebuffer(net, 2), :my_tag)
(src = 1, tag = Symbol(:my_tag)::Tag)

julia> querypop!(messagebuffer(net, 2), :my_tag) === nothing
julia> querydelete!(messagebuffer(net, 2), :my_tag) === nothing
true

julia> querypop!(messagebuffer(net, 2), :another_tag, ❓, ❓)
julia> querydelete!(messagebuffer(net, 2), :another_tag, ❓, ❓)
(src = 1, tag = SymbolIntInt(:another_tag, 123, 456)::Tag)

julia> querypop!(messagebuffer(net, 2), :another_tag, ❓, ❓) === nothing
julia> querydelete!(messagebuffer(net, 2), :another_tag, ❓, ❓) === nothing
true
```

You can also wait on a message buffer for a message to arrive before running a query:

```jldoctes
```jldoctest
julia> net = RegisterNet([Register(3), Register(2), Register(3)])
A network of 3 registers in a graph of 2 edges

Expand All @@ -184,7 +184,7 @@ julia> @resumable function receive_tags(env)
while true
mb = messagebuffer(net, 2)
@yield wait(mb)
msg = querypop!(mb, :second_tag, ❓, ❓)
msg = querydelete!(mb, :second_tag, ❓, ❓)
print("t=$(now(env)): query returns ")
if isnothing(msg)
println("nothing")
Expand Down Expand Up @@ -257,8 +257,8 @@ for (tagsymbol, tagvariant) in pairs(tag_types)
sig_wild = collect(sig)
sig_wild[idx] .= Union{Wildcard,Function}
argssig_wild = [:($a::$t) for (a,t) in zip(args, sig_wild)]
wild_checks = [:(isa($(args[i]),Wildcard) || $(args[i])(tag.data[$i])) for i in idx]
nonwild_checks = [:(tag.data[$i]==$(args[i])) for i in complement_idx]
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}}[]
for (reg_idx, tags) in enumerate(reg.tags)
Expand Down
6 changes: 3 additions & 3 deletions src/tags.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ end
See also: [`query`](@ref), [`tag!`](@ref), [`Wildcard`](@ref)"""
const tag_types = Tag'

Base.getindex(tag::Tag, i::Int) = tag.data[i]
Base.length(tag::Tag) = length(tag.data.data)
Base.iterate(tag::Tag, state=1) = state > length(tag) ? nothing : (tag[state],state+1)
Base.getindex(tag::Tag, i::Int) = SumTypes.unwrap(tag)[i]
Base.length(tag::Tag) = length(SumTypes.unwrap(tag).data)
Base.iterate(tag::Tag, state=1) = state > length(tag) ? nothing : (SumTypes.unwrap(tag)[state],state+1)

function SumTypes.show_sumtype(io::IO, x::Tag)
data = SumTypes.unwrap(x)
Expand Down
Loading