Full API
Autogenerated API list
QuantumSavory.LOG_GROUPS — Constant
Stable log groups emitted by QuantumSavory.
Pass one of these symbols through the logging macro's special _group keyword to let loggers reject a family of records before the message and metadata are constructed.
QuantumSavory.W — Constant
QuantumSavory.AbstractBackground — Type
An abstract type for the various background processes that might be inflicted upon a Register slot, e.g. decay, dephasing, etc.
QuantumSavory.AbstractTag — Type
AbstractTagMarker supertype for named tag heads used by QuantumSavory protocols.
AbstractTag describes the type stored at the head of a typed Tag, such as EntanglementCounterpart in Tag(EntanglementCounterpart, remote_node, remote_slot, pair_id). It does not replace the Tag sum type itself. Generic Tag(::DataType, ...) construction and querying remain available for types that do not subtype AbstractTag.
Custom tag heads supplied through protocol fields declared as Type{<:AbstractTag} must be concrete subtypes of this marker:
struct MyTag <: AbstractTag endQuantumSavory.AmplitudeDamping — Type
struct AmplitudeDamping <: AbstractBackgroundAn amplitude-damping background.
QuantumSavory.ConstantHamiltonianEvolution — Type
struct ConstantHamiltonianEvolution <: AbstractNoninstantOperationRepresents a Hamiltonian being applied for the given duration. See also NonInstantGate.
QuantumSavory.Depolarization — Type
A depolarization background.
The τ parameter specifies the average time between depolarization events (assuming a Poisson point process). I.e. after time t the probability for an depolarization event is 1-exp(-t/τ).
QuantumSavory.HomodyneMeasurement — Type
HomodyneMeasurement(theta)Describe a homodyne measurement on a continuous-variable mode.
theta gives the quadrature angle in radians. For example, 0.0 corresponds to an x-quadrature measurement and pi/2 to a p-quadrature measurement. In the default $\hbar=2$ units, the measured observable is
\[\hat q_\theta = e^{-i\theta}\hat a + e^{i\theta}\hat a^\dagger.\]
For an outcome $q_\theta$, the ideal measurement applies a projector on the following state:
\[|q_\theta;\theta\rangle, \qquad \hat q_\theta |q_\theta;\theta\rangle = q_\theta |q_\theta;\theta\rangle.\]
This quadrature eigenstate is an ideal infinitely squeezed state: its measured quadrature has zero variance and its conjugate quadrature has unbounded variance. project_traceout! returns the real outcome $q_\theta$ and removes the measured mode.
Register-level project_traceout! accepts one qmode slot and one angle.
julia> reg = Register([Qumode()], [GabsRepr(QuadBlockBasis)]);julia> initialize!(reg[1], CoherentState(0.3 + 0.2im));julia> result = project_traceout!(reg[1], HomodyneMeasurement(0.0));QuantumSavory.MessageBuffer — Type
struct MessageBuffer{T}A a buffer for classical messages. Usually a part of a Register structure.
See also: channel, messagebuffer
QuantumSavory.NonInstantGate — Type
struct NonInstantGate <: AbstractNoninstantOperationRepresents an gate applied instantaneously followed by a waiting period. See also ConstantHamiltonianEvolution.
QuantumSavory.PauliNoise — Type
A Pauli noise background.
QuantumSavory.QuantumChannel — Type
struct QuantumChannel{T}Quantum channel for transmitting quantum states from one register to another.
Delay and background noise processes are supported.
The function put! is used to take the contents of a RegRef and put it in the channel. That state can can then be received by a register (after a delay) using the take! method.
julia> using QuantumSavory, ResumableFunctions, ConcurrentSimjulia> regA = Register(1); regB = Register(1);julia> initialize!(regA[1], Z1);julia> sim = Simulation();julia> qc = QuantumChannel(sim, 10.0) # a delay of 10 unitsQuantumChannel{Qubit}(Qubit(), DelayQueue{Register}(ConcurrentSim.QueueStore{Register, Int64}, 10.0), nothing)julia> @resumable function alice_node(env, qc) println("Putting Alice's qubit in the channel at ", now(env)) put!(qc, regA[1]) endalice_node (generic function with 1 method)julia> @resumable function bob_node(env, qc) @yield take!(qc, regB[1]) println("Taking the qubit from alice at ", now(env)) endbob_node (generic function with 1 method)julia> @process alice_node(sim, qc); @process bob_node(sim, qc);julia> run(sim)Putting Alice's qubit in the channel at 0.0Taking the qubit from alice at 10.0julia> regARegister with 1 slots: [ Qubit ] Slots: nothingQuantumSavory.QuantumStateTrait — Type
An abstract type for the various types of states that can be given to Register slots, e.g. qubit, harmonic oscillator, etc.
QuantumSavory.Qubit — Type
struct Qubit <: QuantumStateTraitSpecifies that a given register slot contains qubits.
QuantumSavory.Qumode — Type
struct Qumode <: QuantumStateTraitSpecifies that a given register slot contains qumodes.
QuantumSavory.RegRef — Type
struct RegRefA reference to a Register slot, convenient for use with functions like apply!, etc.
julia> r = Register(2) initialize!(r[1], X₁) observable(r[1], X)0.9999999999999998 + 0.0imQuantumSavory.Register — Type
struct RegisterThe main data structure in QuantumSavory, used to represent a quantum register in an arbitrary formalism.
QuantumSavory.RegisterNet — Type
RegisterNet(graph::SimpleGraph, registers;
classical_delay=0, quantum_delay=0, name=nothing, names=String[])
RegisterNet(registers::Vector{Register};
classical_delay=0, quantum_delay=0, name=nothing, names=String[])Store one Register for each vertex of an undirected SimpleGraph. If graph is omitted, use a chain with one vertex per register.
RegisterNet directly supports these read operations from Graphs.jl: vertices, edges, neighbors, nv, ne, and adjacency_matrix. It is not a subtype of Graphs.AbstractGraph, so other Graphs.jl functions are not part of this interface. Treat the topology as fixed after construction.
Index a network to move from the network to a register or a register slot:
net[i] # Register at vertex inet[i][j] # RegRef for slot j of that registernet[i, j] # the same RegRefnet[:] # all registersnet[:, j] # slot j from every registerThe name keyword gives the network a display name. names gives display names to its registers. These names do not replace the integer graph vertex identifiers. A label can instead be stored as vertex metadata:
net[1, :label] = "left endpoint"Vertex metadata uses net[i, :key]. Undirected edge metadata uses net[(i, j), :key], and directed edge metadata uses net[i => j, :key].
For more sophisticated metadata handling, check out the independent tag and query capabilities of QuantumSavory.
classical_delay and quantum_delay each accept a constant or a callable (src, dst) -> delay. A callable is evaluated in both directions of each edge, so it can give the two directions different delays.
using Graphsgraph = path_graph(3)delay(src, dst) = src < dst ? 0.1 : 0.2net = RegisterNet(graph, [Register(2) for _ in 1:3]; name="line", names=["left", "middle", "right"], classical_delay=delay, quantum_delay=0.05)See Register Networks for the complete explanation.
QuantumSavory.RegisterNet — Method
RegisterNet(
graph::Graphs.SimpleGraphs.SimpleGraph,
registers;
classical_delay,
quantum_delay,
name,
names
) -> RegisterNet
Construct a RegisterNet from a given list of Registers and a graph.
The classical_delay and quantum_delay keyword arguments each accept either a single delay used for every channel or a callable (src, dst) -> delay. The callable is evaluated separately for both directions of every graph edge.
julia> graph = grid([2,2]) # from Graphs.jl{4, 4} undirected simple Int64 graphjulia> registers = [Register(1), Register(2), Register(1), Register(2)]4-element Vector{Register}: Register Register Register Registerjulia> net = RegisterNet(graph, registers)A network of 4 registers in a graph of 4 edgesjulia> neighbors(net, 1) # from Graphs.jl2-element Graphs.FrozenVector{Int64}: 2 3QuantumSavory.RegisterNet — Method
RegisterNet(
registers::Vector{Register};
classical_delay,
quantum_delay,
name,
names
) -> RegisterNet
Construct a RegisterNet from a given list of Registers, defaulting to a chain topology.
julia> net = RegisterNet([Register(2), Register(4), Register(2)])A network of 3 registers in a graph of 2 edgesjulia> neighbors(net,2) # from Graphs.jl2-element Graphs.FrozenVector{Int64}: 1 3QuantumSavory.T1Decay — Type
struct T1Decay <: AbstractBackgroundA background describing the T₁ decay of a two-level system.
QuantumSavory.T1T2Noise — Type
A background combining both T₁ decay and T₂ dephasing.
QuantumSavory.T2Dephasing — Type
struct T2Dephasing <: AbstractBackgroundA background describing the T₂ dephasing of a two-level system.
QuantumSavory.Tag — Type
struct TagTags 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 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:
julia> Tag(:sometagdescriptor, 1, 2, -3)SymbolIntIntInt(:sometagdescriptor, 1, 2, -3)::TagA tag can have a custom DataType as first argument, in which case additional customizability in printing is available. E.g. consider the [EntanglementHistory] tag used to track how pairs were entangled before a swap happened.
julia> using QuantumSavory.ProtocolZoo: EntanglementHistoryjulia> Tag(EntanglementHistory, 1, 2, 3, 4, 5)Was entangled to 1.2 with chunk id 0, but swapped with .5 which was entangled to 3.4 with chunk id 0QuantumInterface.apply! — Method
apply!(
regs::Vector{Register},
indices::Union{Tuple{Vararg{Int64}}, AbstractVector{<:Int64}},
operation;
time
) -> Tuple{Vector{Register}, Any}
Apply a given operation on the given set of register slots.
apply!([regA, regB], [slot1, slot2], Gates.CNOT) would apply a CNOT gate on the content of the given registers at the given slots. The appropriate representation of the gate is used, depending on the formalism under which a quantum state is stored in the given registers. The Hilbert spaces of the registers are automatically joined if necessary.
QuantumInterface.traceout! — Method
traceout!(r::Register, i::Int64) -> Register
Delete one or more register slots.
traceout!(reg, slot) would reset (perform a partial trace) over the given subsystem. The Hilbert space of the register gets automatically shrunk.
traceout!(ref1, ref2, ...) deletes several RegRefs in argument order and returns the corresponding registers as a tuple. When the arguments include every live slot backed by the same StateRef, that state is deleted as one group without calling the backend's partial-trace implementation. Incomplete groups are reduced one slot at a time.
For QuantumMCRepr trajectories, partial reduction samples the discarded subsystem in its native canonical basis. Use project_traceout! instead when the sampled outcome is needed.
QuantumSavory.available_background_types — Function
Return the available public background types along with their documentation.
Used to make a background available to tools like QuantumSavory Studio.
Concrete direct and indirect subtypes of AbstractBackground are discovered on each call. The defining binding of each type must be public. The InteractiveUtils and REPL standard libraries must be loaded to activate this optional method.
QuantumSavory.available_slot_types — Function
Return the available public slot types along with their documentation.
Used to make a slot type available to tools like QuantumSavory Studio.
Concrete direct and indirect subtypes of QuantumStateTrait are discovered on each call. The defining binding of each type must be public. The InteractiveUtils and REPL standard libraries must be loaded to activate this optional method.
QuantumSavory.channel — Method
channel(net::RegisterNet, args...; permit_forward) -> Any
Get a handle to a classical channel between two registers.
Usually used for sending classical messages between registers. It can be used for receiving as well, but a more convenient choice is messagebuffer, which is a message buffer listening to all channels sending to a given destination register.
julia> net = RegisterNet([Register(2), Register(2), Register(2)]) # defaults to a chain topologyA network of 3 registers in a graph of 2 edgesjulia> channel(net, 1=>2)ConcurrentSim.DelayQueue{Tag}(ConcurrentSim.QueueStore{Tag, Int64}, 0.0)julia> channel(net, 1=>2)ConcurrentSim.DelayQueue{Tag}(ConcurrentSim.QueueStore{Tag, Int64}, 0.0)julia> channel(net, 1=>2) === channel(net, net[1]=>net[2])trueSee also: qchannel, messagebuffer
QuantumSavory.constructor_metadata — Function
Return documented constructor fields for a type.
Used to make a constructor available to tools like QuantumSavory Studio.
Each entry has the fields field, type, and doc. Undocumented fields and fields whose names begin with an underscore are omitted. The InteractiveUtils and REPL standard libraries must be loaded to activate this optional method.
QuantumSavory.dist_to_delay — Function
dist_to_delay(distance_m::Real, speed_m_per_s::Real=2.0e8)
dist_to_delay(distances::AbstractDict, speed_m_per_s::Real=2.0e8)Convert a distance in metres to a one-way propagation delay in seconds. The dictionary method returns a new Dict with the same edge keys and converted values.
Distances must be finite and nonnegative. The propagation speed must be finite and positive. The computed delay must also be finite.
julia> dist_to_delay(200_000_000)1.0julia> using Graphsjulia> dist_to_delay(Dict(Edge(1, 2) => 100_000_000))[Edge(1, 2)]0.5QuantumSavory.findfreeslot — Method
findfreeslot(
reg::Register;
chooseslot,
randomize,
locked,
margin
) -> Union{Nothing, RegRef}
Find an empty unlocked slot in a given Register.
julia> reg = Register(3); initialize!(reg[1], X); lock(reg[2]);julia> findfreeslot(reg) == reg[3]truejulia> lock(findfreeslot(reg));julia> findfreeslot(reg) |> isnothingtrueQuantumSavory.generate_map — Function
generate_map([subfig]; extent=nothing, provider=TileProviders.OpenStreetMap())Generates a default map with country and state boundaries and returns a GeoAxis. The returned GeoAxis can be used as an input for registernetplot_axis.
The Tyler package must be installed and imported.
QuantumSavory.initialize! — Method
initialize!(
regs::Union{Tuple{Vararg{Register}}, AbstractVector{<:Register}},
indices::Union{Tuple{Vararg{Int64}}, AbstractVector{<:Int64}},
state;
time
)
Set the state of a given set of registers.
initialize!([regA,regB], [slot1,slot2], state) would set the state of the given slots in the given registers to state. state can be any supported state representation, e.g., kets or density matrices from QuantumOptics.jl or tableaux from QuantumClifford.jl.
QuantumSavory.krausops — Function
For a given background noise type, provide the corresponding Kraus operators, in a QuantumOptics.jl representation.
See also: paulinoise, lindbladop
QuantumSavory.krausops — Method
The Kraus operators for depolarization are √(1-3p/4) I, √p/2 * X, √p/2 * Y, √p/2 Z
QuantumSavory.krausops — Method
The Kraus operators for a T₁ process
A₁ = |0⟩⟨0| + √(1-γ) |1⟩⟨1|A₂ = √γ |0⟩⟨1|λ = 1 - exp(-Δt/T₁)
QuantumSavory.krausops — Method
The Kraus operators for a T₁T₂ process.
Of note, this is not the same as having "on top of each other" T₁ noise and then an additional "dephasing" noise. T₁ is causing dephasing of its own, and T₂ (transverse relaxation time) includes dephasing from T₁ and pure dephasing Tᵩ where 1/Tᵩ = 1/T₂ - 1/(2T₁). See https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/tphi.html for more.
QuantumSavory.krausops — Method
The Kraus operators for a T₂ process
One option is the following (more popular in the literature):
P₁ = |0⟩⟨0| + √(1-λ) |1⟩⟨1|P₂ = √λ |1⟩⟨1|λ = 1 - exp(-2Δt/T₂)
An equivalent option is (more convenient when converting to a Pauli error channel):
P₁′ = √(1-p/2) IP₂′ = √(p/2) Zp = 1 - exp(-Δt/T₂)
These two options are equivalent under a unitary transformation. We implement the second one.
QuantumSavory.lindbladop — Function
For a given background noise type, provide the corresponding Lindblad collapse operator, in a QuantumOptics.jl representation.
See also: paulinoise, krausops
QuantumSavory.lindbladop — Method
1/√τ â
QuantumSavory.lindbladop — Method
1/√T₁ |0⟩⟨1|
QuantumSavory.lindbladop — Method
Lindblad operators for combined T₁ and T₂ noise.
Returns a list of Lindblad operators:
L₁ = (1/√T₁) |0⟩⟨1|for amplitude dampingL₂ = (1/√(2Tᵩ)) Zfor pure dephasing (if T₂ < 2T₁)
where 1/Tᵩ = 1/T₂ - 1/(2T₁)
Of note, this is not the same as having "on top of each other" T₁ noise and then an additional "dephasing" noise. As you can see from the formula above, T₁ is causing dephasing of its own. Thus, T₂ (transverse relaxation time) includes dephasing from T₁ and pure dephasing Tᵩ. See https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/tphi.html for more.
QuantumSavory.lindbladop — Method
1/√(2T₂) Z
QuantumSavory.messagebuffer — Method
messagebuffer(
net::RegisterNet,
dst::Int64
) -> MessageBuffer{Tag}
Get a handle to a classical message buffer corresponding to all channels sending to a given destination register.
See also: channel
QuantumSavory.messagebuffer — Method
messagebuffer(
ref::Union{RegRef, Register}
) -> MessageBuffer{Tag}
Get a handle to a classical message buffer corresponding to all channels sending to a given destination register.
See also: channel
QuantumSavory.network_builder — Method
network_builder(
graph::SimpleGraph,
delays::AbstractDict,
register_args::Tuple;
node_protocols=(),
link_protocols=(),
) -> (; sim, network)Build a register network for graph without running its simulation. One nonempty Register(register_args...) is created per vertex. delays must have exactly one finite, nonnegative value for every undirected graph edge; that value is used for both directions of both the classical and quantum channels.
Each protocol specification is a ProtocolType => NamedTuple. Node protocol types must have :node attachment metadata and are instantiated on every vertex. Link protocol types must have :edge attachment metadata and are instantiated on every undirected edge. sim, net, and the catalog-mapped attachment fields are injected by the builder. All protocols are instantiated before any protocol is scheduled.
julia> using Graphsjulia> graph = path_graph(3);julia> delays = Dict(edge => 0.01 for edge in edges(graph));julia> result = network_builder(graph, delays, (2,));julia> (nv(result.network), length(result.network[1]))(3, 2)QuantumSavory.observable — Method
observable(
regs::Union{Tuple{Vararg{Register}}, AbstractVector{<:Register}},
indices::Union{Tuple{Vararg{Int64}}, AbstractVector{<:Int64}},
obs;
something,
time
) -> Any
Calculate the expectation value of a quantum observable on the given register and slot.
observable([regA, regB], [slot1, slot2], obs) would calculate the expectation value of the obs observable (using the appropriate formalism, depending on the state representation in the given registers).
The register and slot-index collections must have equal lengths, and each physical register slot may appear at most once. Invalid selections are rejected before empty slots are handled, time is advanced, or backend work begins.
QuantumSavory.onchange — Function
onchangeWait for changes to occur on a MessageBuffer or Register. By specifying a second argument, you can filter what type of events are waited on. E.g. onchange(r, Tag) will wait only on changes to tags and metadata.
QuantumSavory.paulinoise — Function
For a given background noise type, provide the corresponding (potentially twirled) Pauli operators and the probabilities for the operators to act, in a QuantumClifford.jl representation.
See also: krausops, lindbladop
QuantumSavory.paulinoise — Method
The Pauli operator and probability of its application for a Depolarization process.
((p/4, X), (p/4, Y), (p/4, Z)) for p = 1-exp(-Δt/τ)
QuantumSavory.paulinoise — Method
The Pauli operator and probability of its application for a T₂ process.
(1-exp(-Δt/T₂)) / 2 and Z
QuantumSavory.project_traceout! — Function
project_traceout!(ref::RegRef, basis; time = nothing)
project_traceout!(reg::Register, i::Int, basis; time = nothing)
project_traceout!(ref::RegRef, basis, values; time = nothing)
project_traceout!(reg::Register, i::Int, basis, values; time = nothing)Perform a projective measurement on the given slot of the given register.
An explicit tuple or vector of orthonormal basis states returns its one-based basis index. Passing a second tuple or vector, values, returns values[index] instead.
A symbolic operator like Pauli operators X, Y, and Z return their eigenvalues, e.g. 1 or -1.
HomodyneMeasurement(θ), where θ is in radians, returns the real measured quadrature qθ = x*cos(θ) + p*sin(θ).
Every successful call removes the measured subsystem and its back-reference. Clifford qubit measurements support the symbolic X, Y, and Z bases; explicit basis vectors are supported by QuantumOptics and QuantumMC.
QuantumSavory.qchannel — Method
qchannel(net::RegisterNet, args...) -> Any
Get a handle to a quantum channel between two registers.
julia> net = RegisterNet([Register(2), Register(2), Register(2)]) # defaults to a chain topologyA network of 3 registers in a graph of 2 edgesjulia> qchannel(net, 1=>2)QuantumChannel{Qubit}(Qubit(), ConcurrentSim.DelayQueue{Register}(ConcurrentSim.QueueStore{Register, Int64}, 0.0), nothing)julia> qchannel(net, 1=>2) === qchannel(net, net[1]=>net[2])trueSee also: channel
QuantumSavory.query — Method
query(
mb::MessageBuffer,
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...
) -> Union{Nothing, NamedTuple{(:depth, :src, :tag), <:Tuple{Int64, Union{Nothing, Int64}, Any}}}
You are advised to actually use querydelete!, not query when working with classical message buffers.
QuantumSavory.query — Method
query(
reg::Union{RegRef, Register},
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...;
locked,
assigned,
filo
) -> Any
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 or the emoji ❓ 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 FILO if not specified. 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).
julia> r = Register(10); tag!(r[1], :symbol, 2, 3); tag!(r[2], :symbol, 4, 5);julia> query(r, :symbol, 4, 5)(slot = 1043859625813851568.2, id = 4, tag = SymbolIntInt(:symbol, 4, 5)::Tag, time = 0.0)julia> lock(r[1]);julia> query(r, :symbol, 4, 5; locked=false) |> isnothingfalsejulia> query(r, :symbol, ❓, 3)(slot = 1043859625813851568.1, id = 3, tag = SymbolIntInt(:symbol, 2, 3)::Tag, time = 0.0)julia> query(r, :symbol, ❓, 3; assigned=true) |> isnothingtruejulia> query(r, :othersym, ❓, ❓) |> isnothingtruejulia> tag!(r[5], Int, 4, 5);julia> query(r, Float64, 4, 5) |> isnothingtruejulia> query(r, Int, 4, >(7)) |> isnothingtruejulia> query(r, Int, 4, <(7))(slot = 1043859625813851568.5, id = 5, tag = TypeIntInt(Int64, 4, 5)::Tag, time = 0.0)A query can be on on a single slot of a register:
julia> r = Register(5);julia> tag!(r[2], :symbol, 2, 3);julia> query(r[2], :symbol, 2, 3)(slot = 2589040728030450388.2, id = 14, tag = SymbolIntInt(:symbol, 2, 3)::Tag, time = 0.0)julia> query(r[3], :symbol, 2, 3) === nothingtruejulia> queryall(r[2], :symbol, 2, 3)1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag, time::Float64}}: (slot = 2589040728030450388.2, id = 14, tag = SymbolIntInt(:symbol, 2, 3)::Tag, time = 0.0)QuantumSavory.query_wait — Function
query_wait(store::Register, args...; on::Type{On}=Any, locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing) where {On}
query_wait(store::MessageBuffer, args...; on::Type{On}=Any) where {On}A convenience function that combines waiting (via onchange) and querying (via query) in a loop, returning a ConcurrentSim process that yields the first successful query result.
This replaces the common pattern of:
while true @yield onchange(register, Tag) result = query(register, :my_tag, ❓) if !isnothing(result) # do something with result break endendwith the much simpler:
result = @yield query_wait(register, :my_tag, ❓)# do something with resultquery_wait does not consume the matching tag. Multiple waiters can observe the same register tag. If your protocol will remove the tag, prefer querydelete_wait!, or re-query/check with querydelete! after acquiring any needed locks.
The on keyword argument is passed to onchange to control what type of events are waited on. The locked and assigned keyword arguments are passed through to query for register queries.
julia> using ResumableFunctions; using ConcurrentSim;julia> reg = Register(5); net = RegisterNet([reg]); env = get_time_tracker(net);julia> @resumable function sender(env, reg) @yield timeout(env, 1.0) tag!(reg[1], :my_tag, 42) end;julia> LOG = [];julia> @resumable function receiver(env, reg) result = @yield query_wait(reg, :my_tag, ❓) push!(LOG, result) end;julia> @process sender(env, reg);julia> @process receiver(env, reg);julia> run(env, 0.5);julia> length(LOG)0julia> run(env, 1.5);julia> length(LOG)1julia> LOG[1].tagSymbolInt(:my_tag, 42)::TagSee also: query, querydelete_wait!, onchange, tag!
QuantumSavory.queryall — Method
queryall(
reg::Union{RegRef, Register},
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...;
filo,
kwargs...
) -> Any
A query function that returns all slots of a register that have a given tag, with support for predicates and wildcards.
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, id::Int128, tag::Tag, time::Float64}}: (slot = 15531193455478883312.2, id = 16, tag = SymbolIntInt(:symbol, 4, 5)::Tag, time = 0.0) (slot = 15531193455478883312.1, id = 15, tag = SymbolIntInt(:symbol, 2, 3)::Tag, time = 0.0)julia> queryall(r, :symbol, ❓, >(4))1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag, time::Float64}}: (slot = 15531193455478883312.2, id = 16, tag = SymbolIntInt(:symbol, 4, 5)::Tag, time = 0.0)julia> queryall(r, :symbol, ❓, >(5))@NamedTuple{slot::RegRef, id::Int128, tag::Tag, time::Float64}[]QuantumSavory.querydelete! — Method
querydelete!(
reg::Union{RegRef, Register},
args...;
kwa...
) -> Any
A query for Register or a register slot (i.e. a RegRef) that also deletes the tag.
For register protocol code, this is safer than query followed by untag!. If the result will be used after an @yield or lock acquisition, re-query after the wait before deleting or acting on the tag.
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, time::Float64}}: (slot = 767672459337976635.2, id = 19, tag = SymbolIntIntInt(:tagA, 10, 20, 30)::Tag, time = 0.0) (slot = 767672459337976635.1, id = 18, tag = SymbolIntIntInt(:tagA, 1, 2, 3)::Tag, time = 0.0)julia> querydelete!(reg, :tagA, ❓, ❓, ❓)(slot = 767672459337976635.2, id = 19, tag = SymbolIntIntInt(:tagA, 10, 20, 30)::Tag, time = 0.0)julia> queryall(reg, :tagA, ❓, ❓, ❓)1-element Vector{@NamedTuple{slot::RegRef, id::Int128, tag::Tag, time::Float64}}: (slot = 767672459337976635.1, id = 18, tag = SymbolIntIntInt(:tagA, 1, 2, 3)::Tag, time = 0.0)QuantumSavory.querydelete! — Method
querydelete!(
mb::MessageBuffer,
queryargs::Union{QuantumSavory.Wildcard, Int64, DataType, Function, Symbol}...
) -> Union{Nothing, @NamedTuple{src::Union{Nothing, Int64}, tag::T} where T}
A query for classical message buffers that also deletes the message out of the buffer.
julia> net = RegisterNet([Register(3), Register(2)])A network of 2 registers in a graph of 1 edgesjulia> put!(channel(net, 1=>2), Tag(:my_tag));julia> put!(channel(net, 1=>2), Tag(:another_tag, 123, 456));julia> query(messagebuffer(net, 2), :my_tag)julia> run(get_time_tracker(net))julia> query(messagebuffer(net, 2), :my_tag)(depth = 1, src = 1, tag = Symbol(:my_tag)::Tag)julia> querydelete!(messagebuffer(net, 2), :my_tag)@NamedTuple{src::Union{Nothing, Int64}, tag::Tag}((1, Symbol(:my_tag)::Tag))julia> querydelete!(messagebuffer(net, 2), :my_tag) === nothingtruejulia> querydelete!(messagebuffer(net, 2), :another_tag, ❓, ❓)@NamedTuple{src::Union{Nothing, Int64}, tag::Tag}((1, SymbolIntInt(:another_tag, 123, 456)::Tag))julia> querydelete!(messagebuffer(net, 2), :another_tag, ❓, ❓) === nothingtrueYou can also wait on a message buffer for a message to arrive before running a query:
julia> using ResumableFunctions; using ConcurrentSim;julia> net = RegisterNet([Register(3), Register(2), Register(3)])A network of 3 registers in a graph of 2 edgesjulia> env = get_time_tracker(net);julia> @resumable function receive_tags(env) while true mb = messagebuffer(net, 2) @yield onchange(mb) 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)") end end endreceive_tags (generic function with 1 method)julia> @resumable function send_tags(env) @yield timeout(env, 1.0) put!(channel(net, 1=>2), Tag(:my_tag)) @yield timeout(env, 2.0) put!(channel(net, 3=>2), Tag(:second_tag, 123, 456)) endsend_tags (generic function with 1 method)julia> @process send_tags(env);julia> @process receive_tags(env);julia> run(env, 10)t=1.0: query returns nothingt=3.0: query returns SymbolIntInt(:second_tag, 123, 456)::Tag received from node 3QuantumSavory.querydelete_wait! — Function
querydelete_wait!(store::Register, args...; on::Type{On}=Any, locked::Union{Nothing,Bool}=nothing, assigned::Union{Nothing,Bool}=nothing) where {On}
querydelete_wait!(store::MessageBuffer, args...; on::Type{On}=Any) where {On}Wait for and remove the first tag matching a query.
A convenience function that combines waiting (via onchange) and querying-with-deletion (via querydelete!) in a loop, returning a ConcurrentSim process that yields the first successful query result (deleting the matched entry).
This replaces the common pattern of:
while true @yield onchange(store, Tag) result = querydelete!(store, :my_tag, ❓) if !isnothing(result) # do something with result break endendwith the much simpler:
result = @yield querydelete_wait!(store, :my_tag, ❓)# do something with resultThe on keyword argument is passed to onchange to control what type of events are waited on. The locked and assigned keyword arguments are passed through to querydelete! for register queries.
julia> using ResumableFunctions; using ConcurrentSim;julia> net = RegisterNet([Register(3), Register(2)]); env = get_time_tracker(net);julia> @resumable function sender(env) @yield timeout(env, 1.0) put!(channel(net, 1=>2), Tag(:my_tag)) @yield timeout(env, 2.0) put!(channel(net, 1=>2), Tag(:second_tag, 123, 456)) end;julia> LOG = [];julia> @resumable function receiver(env) mb = messagebuffer(net, 2) msg = @yield querydelete_wait!(mb, :second_tag, ❓, ❓) push!(LOG, msg) end;julia> @process sender(env);julia> @process receiver(env);julia> run(env, 2.0);julia> length(LOG)0julia> run(env, 4.0);julia> length(LOG)1julia> LOG[1].tagSymbolIntInt(:second_tag, 123, 456)::TagSee also: querydelete!, query_wait, onchange, tag!
QuantumSavory.registernetplot — Function
Draw the given register network.
Requires a Makie backend be already imported.
QuantumSavory.registernetplot! — Function
Draw the given register network on a given Makie axis.
Requires a Makie backend be already imported.
QuantumSavory.registernetplot_axis — Function
registernetplot_axis(registersobservable; kwargs...)Draw the given register network on a given Makie axis or subfigure and modify the axis with numerous visualization enhancements.
Requires a Makie backend be already imported.
QuantumSavory.resourceplot_axis — Function
resourceplot_axis(subfig, network, edgeresources, vertexresources; registercoords=nothing, title="")Draw the various resources and locks stored in the given meta-graph on a given Makie axis.
Requires a Makie backend be already imported.
QuantumSavory.simulation_log_context — Method
simulation_log_context(sim::Simulation)Return the structured logging context for sim.
The result contains the current simulated time and the active ConcurrentSim process identifier. sim_process_id is nothing when called outside a running process.
QuantumSavory.subsystemcompose — Method
Ensure that the all slots of the given registers are represented by one single state object, i.e. that all the register slots are tracked in the same Hilbert space.
QuantumSavory.tag! — Method
QuantumSavory.untag! — Method
untag!(
ref::Union{RegRef, Register},
id::Integer
) -> @NamedTuple{tag::Tag, slot::Int64, time::Float64}
Remove the tag with the given id from a RegRef or a Register.
To remove a tag based on a query, use querydelete! instead. In asynchronous protocols, do not keep a query result across a yield and later call untag! with the old id. Another process may already have consumed that tag. Re-query under the relevant locks or use a consuming query helper.
See also: querydelete!, query, tag!
QuantumSavory.uptotime! — Function
uptotime!Evolve all the states in a register to a given time, according to the various backgrounds that they might have.
julia> reg = Register(2, T1Decay(1.0))Register with 2 slots: [ Qubit | Qubit ] Slots: nothing nothingjulia> initialize!(reg[1], X₁) observable(reg[1], σᶻ)0.0 + 0.0imjulia> uptotime!(reg[1], 10) observable(reg[1], Z)0.9999546000702374 + 0.0im