Predefined Networking Protocols
The submodule QuantumSavory.ProtocolZoo provides reusable networking protocols, including their discrete-event control flow.
What ProtocolZoo Is For
ProtocolZoo contains ready-to-run protocol components such as entanglers, swappers, trackers, consumers, and switch-like controllers.
These are not just code samples. They are structured AbstractProtocol implementations meant to be launched inside a simulation with @process.
prot = EntanglerProt(sim, net, 1, 2)
@process prot()This matters because the protocol object packages:
- the simulation handle,
- the network it acts on,
- the node or nodes it belongs to,
- and the parameters controlling its behavior.
That packaging is what makes protocols easier to reuse and compare than a large free function with many arguments.
How Protocols Compose
Protocols in ProtocolZoo are designed to compose through the same metadata and messaging interfaces available to user-written code.
In practice, that means one protocol can:
- generate entanglement and tag the resulting slots,
- another protocol can query those tags and perform a swap,
- and a tracker or consumer can react to the resulting metadata updates.
This is the practical point of the protocol layer: reusable control logic that does not depend on bespoke peer-to-peer wiring.
Visualization Hooks
Some protocols also expose richer visualization through show methods. EntanglerProt, for example, can render protocol-specific summaries in HTML or PNG form.
Those displays are not part of the protocol logic itself, but they are useful for debugging configuration and inspecting the expected behavior of a protocol before embedding it into a larger simulation.
Typical Contents
The current ProtocolZoo includes:
- entanglement generation and swapping protocols,
- metadata tracking helpers,
- consumer and cutoff protocols,
- switch-style protocols,
- and QTCP-related controllers and message types.
The autodocs below are the exact API reference.
Autogenerated API list for QuantumSavory.ProtocolZoo
QuantumSavory.ProtocolZoo.CutoffProt — Type
struct CutoffProt <: QuantumSavory.ProtocolZoo.AbstractProtocolA protocol running at a node, checking periodically for any qubits in the node that have remained unused for more than the retention period of the qubit and emptying such slots.
If coordination messages are exchanged during deletions (instances of the type EntanglementDelete), then a EntanglementTracker protocol needs to also run, to act on such messages.
sim: time-and-schedule-tracking instance fromConcurrentSimnet: a network graph of registersnode: the vertex index of the node on which the protocol is runningperiod: time period between successive queries on the node (nothingfor queuing up)retention_time: time after which a slot is emptiedannounce: iftrue, synchronization messages are sent after a deletion to the node containing the other entangled qubit
QuantumSavory.ProtocolZoo.EntanglementConsumer — Type
struct EntanglementConsumer <: QuantumSavory.ProtocolZoo.AbstractProtocolA protocol running between two nodes, checking periodically for any entangled pairs between the two nodes and consuming/emptying the qubit slots.
This protocol permits virtual edges, meaning it can operate between any two nodes in the network regardless of whether they are physically connected by an edge.
sim: time-and-schedule-tracking instance fromConcurrentSimnet: a network graph of registersnodeA: the vertex index of node AnodeB: the vertex index of node Bperiod: time period between successive queries on the nodes (nothingfor queuing up and waiting for available pairs)tag: tag type which the consumer is looking for – the consumer query will bequery(node, EntanglementConsumer.tag, remote_node)and it will be expected thatremote_nodepossesses the symmetric reciprocal tag; defaults toEntanglementCounterpart_log: stores the time and resulting observable from querying nodeA and nodeB forEntanglementCounterpart
QuantumSavory.ProtocolZoo.EntanglementCounterpart — Type
struct EntanglementCounterpartIndicates the current entanglement status with a remote node's slot. Added when a new entanglement is generated through EntanglerProt or when a swap happens and the EntanglementTracker receives an [EntanglementUpdate] message.
remote_node::Int64: the id of the remote node to which we are entangledremote_slot::Int64: the slot in the remote node containing the qubit we are entangled to
QuantumSavory.ProtocolZoo.EntanglementHistory — Type
struct EntanglementHistoryThis 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).
remote_node::Int64: the id of the remote node we used to be entangled toremote_slot::Int64: the slot of the remote node we used to be entangled toswap_remote_node::Int64: the id of remote node to which we are entangled after the swapswap_remote_slot::Int64: the slot of the remote node to which we are entangled after the swapswapped_local::Int64: the slot in this register with whom we performed a swap
QuantumSavory.ProtocolZoo.EntanglementTracker — Type
struct EntanglementTracker <: QuantumSavory.ProtocolZoo.AbstractProtocolA 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.
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersnode::Int64: the vertex of the node where the tracker is working
QuantumSavory.ProtocolZoo.EntanglementUpdateX — Type
struct EntanglementUpdateXThis 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.
past_local_node::Int64: the id of the node to which you were entangled before the swappast_local_slot::Int64: the slot of the node to which you were entangled before the swappast_remote_slot::Int64: the slot of your node that we were entangled tonew_remote_node::Int64: the id of the node to which you are now entangled after the swapnew_remote_slot::Int64: the slot of the node to which you are now entangled after the swapcorrection::Int64: what Pauli correction you need to perform
QuantumSavory.ProtocolZoo.EntanglementUpdateZ — Type
struct EntanglementUpdateZThis 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.
past_local_node::Int64: the id of the node to which you were entangled before the swappast_local_slot::Int64: the slot of the node to which you were entangled before the swappast_remote_slot::Int64: the slot of your node that we were entangled tonew_remote_node::Int64: the id of the node to which you are now entangled after the swapnew_remote_slot::Int64: the slot of the node to which you are now entangled after the swapcorrection::Int64: what Pauli correction you need to perform
QuantumSavory.ProtocolZoo.EntanglerProt — Type
struct EntanglerProt <: QuantumSavory.ProtocolZoo.AbstractProtocolA 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.
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersnodeA::Int64: the vertex index of node AnodeB::Int64: the vertex index of node Bpairstate::SymQObj: the state being generated (supports symbolic, numeric, noisy, and pure)success_prob::Float64: success probability of one attempt of entanglement generationattempt_time::Float64: duration of single entanglement attemptlocal_busy_time_pre::Float64: fixed "busy time" duration immediately before starting entanglement generation attemptslocal_busy_time_post::Float64: fixed "busy time" duration immediately after the a successful entanglement generation attemptretry_lock_time::Union{Nothing, Float64}: how long to wait before retrying to lock qubits if no qubits are available (nothingfor queuing up)rounds::Int64: how many rounds of this protocol to run (-1for infinite)attempts::Int64: maximum number of attempts to make per round (-1for infinite)chooseslotA::Union{Int64, Function}: functionInt->Boolor an integer slot number, specifying the slot to take among available free slots in node AchooseslotB::Union{Int64, Function}: functionInt->Boolor an integer slot number, specifying the slot to take among available free slots in node Brandomize::Bool: whether the protocol should find the first available free slots in the nodes to be entangled or check for free slots randomly from the available slotsuselock::Bool: whether the protocol should look for unlocked slots to entangle and lock them during the protocolmargin::Int64: Repeated rounds of this protocol may lead to monopolizing all slots of a pair of registers, starving or deadlocking other protocols. This field can be used to always leave a minimum number of slots free if there already exists entanglement between the current pair of nodes.hardmargin::Int64: Likemargin, but it is enforced even when no entanglement has been established yet. Usually smaller thanmargin.tag::Union{Nothing, DataType}: Tag to be added to the entangled qubits or nothing to not add any tag. The created tag will be of the formtag(remote_node, remote_slot), by defaultEntanglementCounterpart.
QuantumSavory.ProtocolZoo.EntanglerProt — Method
Convenience constructor for specifying rate of generation instead of success probability and time
QuantumSavory.ProtocolZoo.SwapperProt — Type
struct SwapperProt <: QuantumSavory.ProtocolZoo.AbstractProtocolA protocol, running at a given node, that finds swappable entangled pairs and performs the swap.
Consider setting an agelimit on qubits and using it together with the cutoff protocol, CutoffProt, which deletes qubits that are about to go past their cutoff/retention time.
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersnode::Int64: the vertex of the node where swapping is happeningchooseslots::Union{Function, Vector{Int64}}: functionInt->Boolor a vector of allowed slot indices, specifying the slots to take among swappable slots in the nodenodeL::Union{QuantumSavory.Wildcard, Int64, Function}: the vertex of one of the remote nodes for the swap, arbitrarily referred to as the "low" node (or a predicate function or a wildcard); if you are working on a repeater chain, a good choice is<(current_node), i.e. any node to the "left" of the current nodenodeH::Union{QuantumSavory.Wildcard, Int64, Function}: the vertex of the other remote node for the swap, the "high" counterpart ofnodeL; if you are working on a repeater chain, a good choice is>(current_node), i.e. any node to the "right" of the current nodechooseL::Function: thenodeLpredicate can return many positive candidates;chooseLpicks one of them (by index into the array of filterednodeLresults), defaults to a random pickarr->rand(keys(arr)); if you are working on a repeater chain a good choice isargmin, i.e. the node furthest to the "left"chooseH::Function: thenodeHcounterpart forchooseH; if you are working on a repeater chain a good choice isargmax, i.e. the node furthest to the "right"local_busy_time::Float64: fixed "busy time" duration immediately before starting entanglement generation attemptsretry_lock_time::Union{Nothing, Float64}: how long to wait before retrying to lock qubits if no qubits are available (nothingfor queuing up and waiting)rounds::Int64: how many rounds of this protocol to run (-1for infinite))agelimit::Union{Nothing, Float64}: what is the oldest a qubit should be to be picked for a swap (to avoid swapping with qubits that are about to be deleted, the agelimit should be shorter than the retention time of the cutoff protocol) (nothingfor no limit) – you probably want to useCutoffProtif you have an agelimit
QuantumSavory.ProtocolZoo.Switches.SimpleSwitchDiscreteProt — Type
struct SimpleSwitchDiscreteProt <: QuantumSavory.ProtocolZoo.AbstractProtocolA switch "controller", running on a given node, checking for connection requests from neighboring clients, and attempting to serve them by attempting direct raw entanglement with the clients and then mediating swaps to connect two clients together.
Works on discrete time intervals and destroys raw entanglement not used by the end of a ticktock cycle.
This switch is mostly based on the architecture proposed in (Promponas et al., 2024). Multiple switch management algorithms are suggested in that paper. By default we use the QuantumSavory.ProtocolZoo.Switches.promponas_bruteforce_choice algorithm.
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersswitchnode::Int64: the vertex index of the switchclientnodes::Vector{Int64}: the vertex indices of the clientssuccess_probs::Vector{Float64}: best-guess about success of establishing raw entanglement between client and switchticktock::Float64: duration of a single full cycle of the switching decision algorithmrounds::Int64: how many rounds of this protocol to run (-1for infinite)assignment_algorithm::Function: the algorithm to use for memory slot assignment, defaulting topromponas_bruteforce_choice_backlog::QuantumSavory.ProtocolZoo.Switches.SymMatrix{Matrix{Int64}}
QuantumSavory.ProtocolZoo.Switches.SwitchRequest — Type
struct SwitchRequestNotify a switch that you request to be entangled with another node.
requester::Int64: the id of the node making the requestremote_node::Int64: the id of the remote node to which we want to be entangled
QuantumSavory.ProtocolZoo.QTCP.EndNodeController — Type
struct EndNodeController <: QuantumSavory.ProtocolZoo.AbstractProtocolsim: time-and-schedule-tracking instance fromConcurrentSimnet: a network graph of registersnode: the vertex index of where the protocol is located
Managing the following transformations of classical control signals:
Flow@ start node → sequence ofQDatagram@ start nodeQDatagramSuccessreceived from destination node → continuing the flow's sequence, reevaluating window- and converting
LinkLevelReplyAtSourcetoQTCPPairBegin
- and converting
QDatagram@ destination node →QDatagramSuccesssent to start node- and converting
LinkLevelReplyAtHoptoQTCPPairEnd
- and converting
QuantumSavory.ProtocolZoo.QTCP.Flow — Type
struct Flowsrc::Int64: who initiates the request and also initiates the qdatagramsdst::Int64: the destination nodenpairs::Int64: the number of requested pairsuuid::Int64: the unique id of the flow
QuantumSavory.ProtocolZoo.QTCP.LinkController — Type
struct LinkController <: QuantumSavory.ProtocolZoo.AbstractProtocolsim: time-and-schedule-tracking instance fromConcurrentSimnet: a network graph of registersnodeA: the vertex index of one of the nodes in the link (Alice)nodeB: the vertex index of one of the nodes in the link (Bob)
Managing the following transformations of classical control signals:
LinkLevelRequest→LinkLevelReplyandLinkLevelReplyAtHop- this is the entity that actually establishes the link-level entanglement
QuantumSavory.ProtocolZoo.QTCP.LinkLevelReply — Type
struct LinkLevelReplyflow_uuid::Int64: the uuid of the flow we are providing entanglement forseq_num::Int64: sequence number of the qdataframe we are providing entanglement formemory_slot::Int64: where is the entangled qubit stored
QuantumSavory.ProtocolZoo.QTCP.LinkLevelReplyAtHop — Type
struct LinkLevelReplyAtHopflow_uuid::Int64: the uuid of the flow we are providing entanglement forseq_num::Int64: sequence number of the qdataframe we are providing entanglement formemory_slot::Int64: where is the entangled qubit stored
QuantumSavory.ProtocolZoo.QTCP.LinkLevelReplyAtSource — Type
struct LinkLevelReplyAtSourceflow_uuid::Int64: the uuid of the flow we are providing entanglement forseq_num::Int64: sequence number of the qdataframe we are providing entanglement formemory_slot::Int64: where is the entangled qubit stored
QuantumSavory.ProtocolZoo.QTCP.LinkLevelRequest — Type
struct LinkLevelRequestflow_uuid::Int64: the uuid of the flow we are providing entanglement forseq_num::Int64: sequence number of the qdataframe we are providing entanglement forremote_node::Int64: the remote node with which we want to establish entanglement
QuantumSavory.ProtocolZoo.QTCP.NetworkNodeController — Type
struct NetworkNodeController <: QuantumSavory.ProtocolZoo.AbstractProtocolsim: time-and-schedule-tracking instance fromConcurrentSimnet: a network graph of registersnode: the vertex index of where the protocol is located
Managing the following transformations of classical control signals:
QDatagram→LinkLevelRequestto link with next hop (according to a pathfinding algorithm)LinkLevelReply→QDatagramforwarded to next hop and- either a swap with the pre-existing
LinkLevelReplyAtHop - or just tagging
LinkLevelReplyAtSourceif we are at the source node of the flow
- either a swap with the pre-existing
- this is the entity that actually:
- performs pathfinding
- performs entanglement swapping
QuantumSavory.ProtocolZoo.QTCP.QDatagram — Type
struct QDatagramflow_uuid::Int64: the uuid of the flow we are generated forflow_src::Int64: who initiates the flow request and also initiates the qdatagramsflow_dst::Int64: the destination node for the flowcorrection::Int64: the Pauli frame correctionseq_num::Int64: sequence number of the qdataframe in the given flowstart_time::Float64: original start time of the qdatagram
QuantumSavory.ProtocolZoo.QTCP.QTCPPairBegin — Type
struct QTCPPairBeginflow_uuid::Int64: the uuid of the flow we are generated forflow_src::Int64: who initiates the flow request and also initiates the qdatagramsflow_dst::Int64: the destination node for the flowseq_num::Int64: sequence number of the qdataframe in the given flowmemory_slot::Int64: the memory slot of the entangled qubitstart_time::Float64: original start time of the qdatagram
QuantumSavory.ProtocolZoo.QTCP.QTCPPairEnd — Type
struct QTCPPairEndflow_uuid::Int64: the uuid of the flow we are generated forflow_src::Int64: who initiates the flow request and also initiates the qdatagramsflow_dst::Int64: the destination node for the flowseq_num::Int64: sequence number of the qdataframe in the given flowmemory_slot::Int64: the memory slot of the entangled qubitstart_time::Float64: original start time of the qdatagram
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.GraphStateConstructor — Type
struct GraphStateConstructor <: QuantumSavory.ProtocolZoo.AbstractProtocolA graph state constructor protocol. For a given graph state with n vertices, and n registers each containing a communication qubit and a storage qubit, perform Bell pair entanglement distribution (in the order of rounds prescribed by graph_builder), followed by fusion.
Currently the process is not dynamically adjusted (e.g. due to failure to establish a Bell pair) and each Bell pair generation is repeatedly attempted until it succeeds.
For example, constructing this graph will require the following steps:
4
|
3
/ 1───2- entanglers running on 3-4 and 1-2
- only after both entanglers succeed, the states of the comm qubits at 1,2,3,4 are moved into the storage qubits
- entanglers running on 1-3
- fusing from the comm qubits into the storage qubits is executed at 1 and 3
- entanglers running on 2-3
- fusing from the comm qubits into the storage qubits is executed at 2 and 3
Opportunities for improvement:
- if one of the links in a given round succeeds first, we should execute the corresponding fusion into storage qubits immediately. I.e. if 3-4 succeeds before 1-2, the fusion at 3 and 4 should not wait for the entangler between 1 and 2.
- if one of the links succeeds before another link in the same round, permit other entanglers to run. I.e. if 1-2 succeeds before 3-4, rerun the edge search (in this particular example there is nothing to do, but that is not always the case).
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersgraph::Graphs.AbstractGraph: the graph state to be constructed, with vertices corresponding to entries innodesnodes::Vector{Int64}: nodes at which the graph state is distributedcommunication_slot::Int64: slot for entanglement generation (e.g. electron spin)storage_slot::Int64: slot for storage (e.g. nuclear spin)
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.GraphToResource — Type
struct GraphToResource <: QuantumSavory.ProtocolZoo.AbstractProtocolApply local operations to a graph state to convert it to a locally-equivalent general stabilizer state.
It is parameterized by the indices of the Hadamard, inverse Phase, and Z gates that need to be performed, e.g. as provided by the graphstate function in QuantumClifford.jl.
There are constraints to how this protocol works, chiefly it is an "instant classical communication" protocol. It is useful in situations where all "registers" or "nodes" are in the same fridge, controlled by a single controller.
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersnodes::Vector{Int64}: nodes at which the graph state is distributedslot::Int64: slot at each node where the graph state qubit is storedhadamard_idx::Vector{Int64}: indices where Hadamard corrections are to be appliediphase_idx::Vector{Int64}: indices where inverse Phase corrections are to be appliedflips_idx::Vector{Int64}: indices where Z corrections are to be applied
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.MBQCPurificationTracker — Type
TYPEDEF
Track results of Bell measurements sent from other locations, deciding how to proceed. The two options are:
- success: tag the purified Bell pairs with
PurifiedEntanglementCounterparttag - failure: clean up all involved qubit slots
TYPEDFIELDS
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.PurifiedEntanglementCounterpart — Type
struct PurifiedEntanglementCounterpartA tag indicating a purified entanglement with a remote node.
remote_node::Int64: the remote node we are entangled to after purificationremote_slot::Int64: the slot in the remote node
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.PurifierBellMeasurementResults — Type
struct PurifierBellMeasurementResultsMessage containing the results of Bell measurements performed during purification.
node::Int64: the node that performed the measurementsmeasurements_XX::Int64: bit-packed XX measurement resultsmeasurements_ZZ::Int64: bit-packed ZZ measurement results
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.PurifierBellMeasurements — Type
struct PurifierBellMeasurements <: QuantumSavory.ProtocolZoo.AbstractProtocolApply Bell measurements to a number of local nodes, bitpack the results in a single Int64 and send that information to a remote location.
There are constraints to how this protocol works, chiefly it is an "instant classical communication" protocol. It is useful in situations where all "registers" or "nodes" are in the same fridge, controlled by a single controller.
sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance fromConcurrentSimnet::RegisterNet: a network graph of registersnodes::Vector{Int64}: nodes at which the Bell measurements will be happeninglocal_chief_idx::Int64: "Chief" node for our local set of nodes, the source of the bitpacked messageremote_chief_idx::Int64: "Chief" node for the remote set of nodes, the destination node for the bitpacked messagex_slot::Int64: slot on which the X measurement is performed (same for all nodes), the control of the CNOTz_slot::Int64: slot on which the Z measurement is performed (same for all nodes), the target of the CNOT
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.graph_builder — Method
graph_builder(g::Graph)A re-entrant graph-state compilation steps generator. Returns lists of edges that can be entangled in parallel by using maximal cardinality matching on the given graph.
Consider this graph where 1-2 and 3-4 can be created in parallel:
4
|
3
/ 1───2Prepare the compiler by specifying the graph state to be generated:
julia> g = Graph(4); for ij in [(1,2),(2,3),(1,3),(3,4)] add_edge!(g, ij...) end
julia> step_gen = graph_builder(g);Then execute it once in order to get the first round of edges that need to be entangled:
julia> step_gen()
2-element Vector{Tuple{Int64, Int64}}:
(1, 2)
(3, 4)
julia> step_gen()
1-element Vector{Tuple{Int64, Int64}}:
(2, 3)
julia> step_gen()
1-element Vector{Tuple{Int64, Int64}}:
(1, 3)
julia> step_gen() |> isnothing # the generator returns `nothing` when done
trueImportantly, if the link generation is probabilistic and only part of the links succeed, you can provide that information back to the generator, so that it can account for failed attempts:
julia> g = Graph(4); for ij in [(1,2),(2,3),(1,3),(3,4)] add_edge!(g, ij...) end
julia> step_gen = graph_builder(g);
julia> step_gen()
2-element Vector{Tuple{Int64, Int64}}:
(1, 2)
(3, 4)
julia> step_gen([(3,4)]) # assume only 3-4 was successfully generated
1-element Vector{Tuple{Int64, Int64}}:
(2, 3)
julia> step_gen()
1-element Vector{Tuple{Int64, Int64}}:
(1, 2)
julia> step_gen()
1-element Vector{Tuple{Int64, Int64}}:
(1, 3)
julia> step_gen() |> isnothing # the generator returns `nothing` when done
true