diff --git a/Project.toml b/Project.toml index 05e0e0e4..348f77d2 100644 --- a/Project.toml +++ b/Project.toml @@ -53,5 +53,5 @@ Random = "1" Reexport = "1.2.2" ResumableFunctions = "0.6" Statistics = "1" -SumTypes = "0.5.1" +SumTypes = "0.5.5" julia = "1.9" diff --git a/docs/make.jl b/docs/make.jl index adc93d8b..81ff0523 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -13,7 +13,7 @@ function main() plugins = [bib], doctest = false, clean = true, - warnonly = true, # TODO [:missing_docs], + warnonly = [:missing_docs], sitename = "QuantumSavory.jl", format = Documenter.HTML( assets=["assets/init.js"] @@ -25,12 +25,11 @@ function main() "Getting Started Manual" => "manual.md", "Explanations" => [ "explanations.md", + "Register Interface" => "register_interface.md", "Properties and Backgrounds" => "propbackgrounds.md", "Symbolic Expressions" => "symbolics.md", + "Tagging and Querying" => "tag_query.md", "Visualizations" => "visualizations.md", - "Dev Documentation" => [ - "Register Interface" => "register_interface.md", - ], ], "How-To Guides" => [ "howto.md", @@ -49,6 +48,7 @@ function main() "API" => "API.md", "CircuitZoo API" => "API_CircuitZoo.md", "StatesZoo API" => "API_StatesZoo.md", + "ProtocolZoo API" => "API_ProtocolZoo.md", "Bibliography" => "bibliography.md", ], ] diff --git a/docs/src/API_ProtocolZoo.md b/docs/src/API_ProtocolZoo.md new file mode 100644 index 00000000..d47f4f13 --- /dev/null +++ b/docs/src/API_ProtocolZoo.md @@ -0,0 +1,26 @@ +# Available Protocols + +```@raw html + +``` + +## Autogenerated API list for `QuantumSavory.ProtocolZoo` + +```@autodocs +Modules = [QuantumSavory.ProtocolZoo] +Private = false +``` + +## Non-exported custom tags used by these protocols + +```@docs +QuantumSavory.ProtocolZoo.EntanglementCounterpart +QuantumSavory.ProtocolZoo.EntanglementHistory +QuantumSavory.ProtocolZoo.EntanglementUpdateX +QuantumSavory.ProtocolZoo.EntanglementUpdateZ +``` \ No newline at end of file diff --git a/docs/src/explanations.md b/docs/src/explanations.md index 3c49e5d1..1e5a85fd 100644 --- a/docs/src/explanations.md +++ b/docs/src/explanations.md @@ -12,6 +12,7 @@ You probably want to cover the: - `Register` and `RegisterNet` structures - basic interface used by the library - symbolic capabilities for formalism-agnostic description +- metadata tagging and metadata queries for interoperability between protocols - available simulator backends - discrete event simulations and time-tracking capabilities - background noise processes diff --git a/docs/src/references.bib b/docs/src/references.bib index 7e774b61..378cdaf2 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -211,7 +211,7 @@ @article{keisuke2009doubleselection journal={Phys. Rev. A 80, 042308}, year={2009}, url={https://doi.org/10.1103/PhysRevA.80.042308}, - doi={https://doi.org/10.1103/PhysRevA.80.042308} + doi={10.1103/PhysRevA.80.042308} } @article{krastanov2019optimised, diff --git a/docs/src/register_interface.md b/docs/src/register_interface.md index b80556ce..34b7f082 100644 --- a/docs/src/register_interface.md +++ b/docs/src/register_interface.md @@ -12,6 +12,10 @@ A rather diverse set of simulation libraries is used under the hood. Long term t Initialize the state of a register to a known state. +```@docs; canonical=false +initialize! +``` + #### `initialize!(refs::Vector{RegRef}, state; time)` Store a `state` in the given register slots. @@ -66,6 +70,10 @@ flowchart TB Apply a quantum operation to a register. +```@docs; canonical=false +apply! +``` + #### `apply!(refs::Vector{RegRef}, operation; time)` Applying an `operation` to the qubits referred to by the sequence of [`RegRef`](@ref)s at a specified `time`. @@ -126,6 +134,10 @@ flowchart TB Measure a quantum observable. The dispatch down the call three is very similar to the one for `apply!`. +```@docs; canonical=false +observable +``` + #### `observable(refs::Tuple{Vararg{RegRef, N}}, obs, something=nothing; time)` Calculate the value of an observable on the state in the sequence of [`RegRef`](@ref)s at a specified `time`. If these registers are not instantiated, return `something`. @@ -180,6 +192,10 @@ flowchart TB ## `project_traceout!` +```@docs; canonical=false +project_traceout! +``` + #### `project_traceout!(r::RegRef, basis; time)` Project the state in `RegRef` on `basis` at a specified `time`. `basis` can be a `Vector` or `Tuple` of basis states, or it can be a `Matrix` like `Z` or `X`. @@ -228,6 +244,10 @@ flowchart TB ## `traceout!` +```@docs; canonical=false +traceout! +``` + Perform a partial trace over a part of the system (i.e. discard a part of the system). #### `traceout!(r::RegRef)` @@ -257,6 +277,10 @@ flowchart TB ## `uptotime!` +```@docs; canonical=false +uptotime! +``` + #### `uptotime!(ref::RegRef, now)` Evolve the state in a `RegRef` upto a given time `now` diff --git a/docs/src/tag_query.md b/docs/src/tag_query.md new file mode 100644 index 00000000..7bc17fdc --- /dev/null +++ b/docs/src/tag_query.md @@ -0,0 +1,75 @@ +# Tagging and Querying + +```@meta +DocTestSetup = quote + using QuantumSavory +end +``` + +The [`query`](@ref) and [`tag!`](@ref) interface lets you manage "classical state" metadata in your simulations. In particular, this interface enables the creation of modular interoperable [control protocols](@ref "Available Protocols"). Each protocol can operate independently of others without knowledge of each others' internals. This is done by using various "tags" to communicate metadata between the network nodes running the protocols, and by the protocols querying for the presence of such tags, leading to greater flexibility when setting up different simulations. + +The components of the query interface which make this possible are described below. + +## The `Tag` type + +```@docs; canonical=false +QuantumSavory.Tag +``` + +And here are all currently supported tag signatures: + +```@example +using QuantumSavory #hide +[tuple(m.sig.types[2:end]...) for m in methods(Tag) if m.sig.types[2] ∈ (Symbol, DataType)] +``` + +## Assigning and removing tags + +```@docs; canonical=false +QuantumSavory.tag! +QuantumSavory.untag! +``` + +## Querying for the pressence of a tag + +The [`query`](@ref) function allows the user to query for [`Tag`](@ref)s in three different cases: +- on a particular qubit slot ([`RegRef`](@ref)) in a [`Register`](@ref) node; +- on a [`Register`](@ref) to query for any slot that contains the passed `Tag`; +- on a [`messagebuffer`](@ref) to query for a particular `Tag` received from another node in a network. + +The `Tag` description passed to `query` can include predicate functions (of the form `x -> pass::Bool`) and wildcards (the [`❓`](@ref) variable), for situations where we have freedom in what tag we are exactly searching for. + +The queries can search in `FIFO` or `FILO` order (`FILO` by default). E.g., for the default `FILO`, a query on a [`RegRef`](@ref) returns the `Tag` which is at the end of the vector of tags stored the given slot (as new tags are appended at the end). On a [`Register`](@ref) it returns the slot with the "youngest" age. + +One can also query by "lock" and "assignment" status of a given slot, by using the `locked` and `assigned` boolean keywords. By default these keywords are set to `nothing` and these properties are not checked. + +Following is a detailed description of each `query` methods + +```@docs; canonical=false +query(::Register,::Tag) +query(::RegRef,::Tag) +query(::MessageBuffer,::Tag) +``` + +### Wildcards + +```@docs; canonical=false +W +❓ +``` + +### `querydelete!` + +A method on top of [`query`](@ref), which allows to query for tag in a [`RegRef`](@ref) or a [`messagebuffer`](@ref), returning the tag that satisfies the passed predicates and wildcars, **and deleting it from the list at the same time**. It otherwise has the same signature as [`query`](@ref). + +```@docs; canonical=false +querydelete!(::RegRef) +querydelete!(::MessageBuffer) +``` + +### `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 query. + +```@docs; canonical=false +QuantumSavory.queryall +``` \ No newline at end of file diff --git a/docs/src/tutorial/message_queues.md b/docs/src/tutorial/message_queues.md index e2733973..50b00320 100644 --- a/docs/src/tutorial/message_queues.md +++ b/docs/src/tutorial/message_queues.md @@ -132,22 +132,22 @@ time_before_success = now(sim) ## Communication delay -Classical communication delay might be important too. There are FILO storage stacks that can simulate that, e.g. `DelayChannel(sim, delay_time)` used instead of `Storage(sim)`. Below we augment the example from above with such a delay channel and we also add some crude instrumentation and plotting. +Classical communication delay might be important too. There are FILO storage stacks that can simulate that, e.g. `DelayQueue(sim, delay_time)` used instead of `Storage(sim)`. Below we augment the example from above with such a delay channel and we also add some crude instrumentation and plotting. ```@example messagechannel sim = Simulation() communication_delay = 1.0 -channel_1to2 = DelayChannel{Bool}(sim, communication_delay) -channel_2to1 = DelayChannel{Bool}(sim, communication_delay) -channel_ready = DelayChannel{Bool}(sim, communication_delay) +channel_1to2 = DelayQueue{Bool}(sim, communication_delay) +channel_2to1 = DelayQueue{Bool}(sim, communication_delay) +channel_ready = DelayQueue{Bool}(sim, communication_delay) global_log = [] @resumable function do_random_measurement_transmit_receive_compare(sim, channel_out, channel_in) @yield timeout(sim, 2+rand()) # wait for the measurement to take place local_measurement = rand() < 0.4 # simulate a random measurement result - put(channel_out, local_measurement) - other_measurement = @yield get(channel_in) + put!(channel_out, local_measurement) + other_measurement = @yield take!(channel_in) succeeded = local_measurement == other_measurement == true return succeeded end @@ -156,7 +156,7 @@ end s = now(sim) reset_duration = 2.0 @yield timeout(sim, reset_duration) - put(channel_ready, true) + put!(channel_ready, true) push!(global_log, (:reset_system, s, now(sim))) end @@ -183,7 +183,7 @@ end end push!(global_log, (:node_2_meas_tx_rx, s, now(sim))) s2 = now(sim) - @yield get(channel_ready) + @yield take!(channel_ready) push!(global_log, (:node_2_wait_for_reset, s2, now(sim))) end end diff --git a/src/ProtocolZoo/ProtocolZoo.jl b/src/ProtocolZoo/ProtocolZoo.jl index bf4469e1..a19edc9f 100644 --- a/src/ProtocolZoo/ProtocolZoo.jl +++ b/src/ProtocolZoo/ProtocolZoo.jl @@ -22,40 +22,94 @@ get_time_tracker(prot::AbstractProtocol) = prot.sim Process(prot::AbstractProtocol, args...; kwargs...) = Process((e,a...;k...)->prot(a...;k...,_prot=prot), get_time_tracker(prot), args...; kwargs...) +""" +$TYPEDEF + +Indicates the current entanglement status with a remote node's slot. Added when a new entanglement is generated through [`EntanglerProt`](@ref) or when a swap happens and + the [`EntanglementTracker`](@ref) receives an [`EntanglementUpdate`] message. + +$TYPEDFIELDS +""" @kwdef struct EntanglementCounterpart + "the id of the remote node to which we are entangled" remote_node::Int + "the slot in the remote node containing the qubit we are entangled to" remote_slot::Int end 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) +""" +$TYPEDEF + +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. +It helps in situations when locally we have performed a swap, but we are now receiving a message +from a distant node that does not know yet that the swap has occurred (thus the distant node might +have outdated information about who is entangled to whom and we need to update that information). + +$TYPEDFIELDS +""" @kwdef struct EntanglementHistory + "the id of the remote node we used to be entangled to" remote_node::Int + "the slot of the remote node we used to be entangled to" remote_slot::Int + "the id of remote node to which we are entangled after the swap" swap_remote_node::Int + "the slot of the remote node to which we are entangled after the swap" swap_remote_slot::Int + "the slot in this register with whom we performed a swap" 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)") Tag(tag::EntanglementHistory) = Tag(EntanglementHistory, tag.remote_node, tag.remote_slot, tag.swap_remote_node, tag.swap_remote_slot, tag.swapped_local) +""" +$TYPEDEF + +This tag arrives as a message from a remote node to which the current node was entangled to update the +entanglement information and apply an `X` correction after the remote node performs an entanglement swap. + +$TYPEDFIELDS +""" @kwdef struct EntanglementUpdateX + "the id of the node to which you were entangled before the swap" past_local_node::Int + "the slot of the node to which you were entangled before the swap" past_local_slot::Int + "the slot of your node that we were entangled to" past_remote_slot::Int + "the id of the node to which you are now entangled after the swap" new_remote_node::Int + "the slot of the node to which you are now entangled after the swap" new_remote_slot::Int + "what Pauli correction you need to perform" 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 Z$(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) +""" +$TYPEDEF + +This tag arrives as a message from a remote node to which the current node was entangled to update the +entanglement information and apply a `Z` correction after the remote node performs an entanglement swap. + +$TYPEDFIELDS +""" @kwdef struct EntanglementUpdateZ + "the id of the node to which you were entangled before the swap" past_local_node::Int + "the slot of the node to which you were entangled before the swap" past_local_slot::Int + "the slot of your node that we were entangled to" past_remote_slot::Int + "the id of the node to which you are now entangled after the swap" new_remote_node::Int + "the slot of the node to which you are now entangled after the swap" new_remote_slot::Int + "what Pauli correction you need to perform" 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 X$(tag.correction)") @@ -68,7 +122,7 @@ A protocol that generates entanglement between two nodes. Whenever a pair of empty slots is available, the protocol locks them and starts probabilistic attempts to establish entanglement. -$FIELDS +$TYPEDFIELDS """ @kwdef struct EntanglerProt{LT} <: AbstractProtocol where {LT<:Union{Float64,Nothing}} """time-and-schedule-tracking instance from `ConcurrentSim`""" @@ -147,7 +201,7 @@ $TYPEDEF A protocol, running at a given node, that finds swappable entangled pairs and performs the swap. -$FIELDS +$TYPEDFIELDS """ @kwdef struct SwapperProt{NL,NH,CL,CH,LT} <: AbstractProtocol where {NL<:Union{Int,<:Function,Wildcard}, NH<:Union{Int,<:Function,Wildcard}, CL<:Function, CH<:Function, LT<:Union{Float64,Nothing}} """time-and-schedule-tracking instance from `ConcurrentSim`""" @@ -237,7 +291,7 @@ $TYPEDEF A protocol, running at a given node, listening for messages that indicate something has happened to a remote qubit entangled with one of the local qubits. -$FIELDS +$TYPEDFIELDS """ @kwdef struct EntanglementTracker <: AbstractProtocol """time-and-schedule-tracking instance from `ConcurrentSim`""" diff --git a/src/QuantumSavory.jl b/src/QuantumSavory.jl index d3b5b6d7..b6928b1b 100644 --- a/src/QuantumSavory.jl +++ b/src/QuantumSavory.jl @@ -2,6 +2,7 @@ module QuantumSavory using Reexport +using DocStringExtensions using IterTools using LinearAlgebra using Random: randperm @@ -49,6 +50,8 @@ export uptotime!, overwritetime!, # tags.jl and queries.jl Tag, tag!, untag!, W, ❓, query, queryall, querydelete!, findfreeslot, + #messagebuffer.jl + MessageBuffer, # quantumchannel.jl QuantumChannel, # backgrounds.jl diff --git a/src/baseops/traceout.jl b/src/baseops/traceout.jl index 09361209..f8049e67 100644 --- a/src/baseops/traceout.jl +++ b/src/baseops/traceout.jl @@ -56,11 +56,7 @@ Perform a projective measurement on the given slot of the given register. `project_traceout!(reg, slot, [stateA, stateB])` performs a projective measurement, projecting on either `stateA` or `stateB`, returning the index of the subspace on which the projection happened. It assumes the list of possible states forms a basis -<<<<<<< HEAD -for the Hilbert space. The Hilbert space of the register gets automatically shrunk. -======= for the Hilbert space. The Hilbert space of the register is automatically shrunk. ->>>>>>> 2285fd9 (spelling fixes) A basis object can be specified on its own as well, e.g. `project_traceout!(reg, slot, basis)`. diff --git a/src/queries.jl b/src/queries.jl index 9d58530e..9ca6c085 100644 --- a/src/queries.jl +++ b/src/queries.jl @@ -1,34 +1,59 @@ -"""Assign a tag to a slot in a register. +"""$TYPEDSIGNATURES -See also: [`query`](@ref)""" +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) end 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 end -"""Wildcard for use with the tag querying functionality. + +"""Wildcard type for use with the tag querying functionality. + +Usually you simply want an instance of this type (available as the constant [`W`](@ref) or [`❓`](@ref)). See also: [`query`](@ref), [`tag!`](@ref)""" struct Wildcard end + """A wildcard instance for use with the tag querying functionality. -See also: [`query`](@ref), [`tag!`](@ref), [`Wildcard`](@ref)""" +See also: [`query`](@ref), [`tag!`](@ref), [`❓`](@ref)""" const W = Wildcard() + """A wildcard instance for use with the tag querying functionality. -See also: [`query`](@ref), [`tag!`](@ref), [`Wildcard`](@ref)""" +This emoji can be inputted with the `\\:question:` emoji shortcut, +or you can simply use the ASCII alternative [`W`](@ref). + +See also: [`query`](@ref), [`tag!`](@ref), [`W`](@ref)""" const ❓ = W -""" A query function that returns all slots of a register that have a given tag, with support for predicates and wildcards. + +""" +$TYPEDSIGNATURES + +A query function that returns all slots of a register that have a given tag, with support for predicates and wildcards. ```jldoctest julia> r = Register(10); @@ -51,7 +76,10 @@ julia> queryall(r, :symbol, ❓, >(5)) queryall(args...; kwargs...) = query(args..., Val{true}(); kwargs...) -""" A query function searching for the first slot in a register that has a given tag. +""" +$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). @@ -93,7 +121,7 @@ julia> query(r, Int, 4, <(7)) (slot = Slot 5, tag = TypeIntInt(Int64, 4, 5)::Tag) ``` -See also: [`queryall`](@ref), [`tag!`](@ref), [`Wildcard`](@ref) +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 @@ -106,7 +134,12 @@ function query(reg::Register, tag::Tag, ::Val{allB}=Val{false}(); locked::Union{ end end -"""A [`query`](@ref) on a single slot of a register. + + +""" +$TYPEDSIGNATURES + +A [`query`](@ref) on a single slot of a register. ```jldoctest julia> r = Register(5); @@ -134,15 +167,23 @@ function query(ref::RegRef, tag::Tag, ::Val{allB}=Val{false}()) where {allB} # T end end -"""A [`query`](@ref) for classical message buffers. -You are advised to actually use [`querypop!`](@ref), not `query` when working with classical message buffers.""" -function query(mb::MessageBuffer, tag::Tag) +""" +$TYPEDSIGNATURES + +You are advised to actually use [`querydelete!`](@ref), not `query` when working with classical message buffers. +""" +function query(mb::MessageBuffer, tag::Tag, ::Val{allB}=Val{false}()) where {allB} i = findfirst(t->t.tag==tag, mb.buffer) 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. + + +""" +$TYPEDSIGNATURES + +A [`query`](@ref) for classical message buffers that also deletes the message out of the buffer. ```jldoctest julia> net = RegisterNet([Register(3), Register(2)]) @@ -159,22 +200,24 @@ 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> using ResumableFunctions; using ConcurrentSim; + julia> net = RegisterNet([Register(3), Register(2), Register(3)]) A network of 3 registers in a graph of 2 edges @@ -184,12 +227,12 @@ julia> @resumable function receive_tags(env) 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, ❓, ❓) + print("t=\$(now(env)): query returns ") if isnothing(msg) println("nothing") else - println("$(msg.tag) received from node $(msg.src)") + println("\$(msg.tag) received from node \$(msg.src)") end end end @@ -217,6 +260,13 @@ 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`. +""" 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) @@ -303,6 +353,8 @@ for (tagsymbol, tagvariant) in pairs(tag_types) end end + + """Find an empty unlocked slot in a given [`Register`](@ref). ```jldoctest @@ -329,6 +381,7 @@ function findfreeslot(reg::Register; randomize=false) end 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 end diff --git a/src/tags.jl b/src/tags.jl index 8af53368..374d3e77 100644 --- a/src/tags.jl +++ b/src/tags.jl @@ -1,3 +1,24 @@ +""" +Tags are used to represent classical metadata describing the state (or even history) of nodes and their registers. The library allows the construction of custom tags using the `Tag` constructor. Currently tags are implemented as instances of a [sum type](https://github.com/MasonProtter/SumTypes.jl) and have fairly constrained structure. Most of them are constrained to contain only Symbol instances and integers. + +Here is an example of such a generic tag: + +```jldoctest +julia> Tag(:sometagdescriptor, 1, 2, -3) +SymbolIntIntInt(:sometagdescriptor, 1, 2, -3)::Tag +``` + +A tag can have a custom `DataType` as first argument, in which case additional customability in printing is available. E.g. consider the [`EntanglementHistory`] tag used to track how pairs were entangled before a swap happened. + +```jldoctest +julia> using QuantumSavory.ProtocolZoo: EntanglementHistory + +julia> Tag(EntanglementHistory, 1, 2, 3, 4, 5) +Was entangled to 1.2, but swapped with .5 which was entangled to 3.4 +``` + +See also: [`tag!`](@ref), [`query`](@ref) +""" @sum_type Tag :hidden begin Symbol(::Symbol) SymbolInt(::Symbol, ::Int)