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.CutoffProtType
struct CutoffProt <: QuantumSavory.ProtocolZoo.AbstractProtocol

A 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 from ConcurrentSim

  • net: a network graph of registers

  • node: the vertex index of the node on which the protocol is running

  • period: time period between successive queries on the node (nothing for queuing up)

  • retention_time: time after which a slot is emptied

  • announce: if true, synchronization messages are sent after a deletion to the node containing the other entangled qubit

source
QuantumSavory.ProtocolZoo.EntanglementConsumerType
struct EntanglementConsumer <: QuantumSavory.ProtocolZoo.AbstractProtocol

A 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 from ConcurrentSim

  • net: a network graph of registers

  • nodeA: the vertex index of node A

  • nodeB: the vertex index of node B

  • period: time period between successive queries on the nodes (nothing for queuing up and waiting for available pairs)

  • tag: tag type which the consumer is looking for – the consumer query will be query(node, EntanglementConsumer.tag, remote_node) and it will be expected that remote_node possesses the symmetric reciprocal tag; defaults to EntanglementCounterpart

  • _log: stores the time and resulting observable from querying nodeA and nodeB for EntanglementCounterpart

source
QuantumSavory.ProtocolZoo.EntanglementCounterpartType
struct EntanglementCounterpart

Indicates 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 entangled

  • remote_slot::Int64: the slot in the remote node containing the qubit we are entangled to

source
QuantumSavory.ProtocolZoo.EntanglementHistoryType
struct EntanglementHistory

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).

  • remote_node::Int64: the id of the remote node we used to be entangled to

  • remote_slot::Int64: the slot of the remote node we used to be entangled to

  • swap_remote_node::Int64: the id of remote node to which we are entangled after the swap

  • swap_remote_slot::Int64: the slot of the remote node to which we are entangled after the swap

  • swapped_local::Int64: the slot in this register with whom we performed a swap

source
QuantumSavory.ProtocolZoo.EntanglementTrackerType
struct EntanglementTracker <: QuantumSavory.ProtocolZoo.AbstractProtocol

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.

  • sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • node::Int64: the vertex of the node where the tracker is working

source
QuantumSavory.ProtocolZoo.EntanglementUpdateXType
struct EntanglementUpdateX

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.

  • past_local_node::Int64: the id of the node to which you were entangled before the swap

  • past_local_slot::Int64: the slot of the node to which you were entangled before the swap

  • past_remote_slot::Int64: the slot of your node that we were entangled to

  • new_remote_node::Int64: the id of the node to which you are now entangled after the swap

  • new_remote_slot::Int64: the slot of the node to which you are now entangled after the swap

  • correction::Int64: what Pauli correction you need to perform

source
QuantumSavory.ProtocolZoo.EntanglementUpdateZType
struct EntanglementUpdateZ

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.

  • past_local_node::Int64: the id of the node to which you were entangled before the swap

  • past_local_slot::Int64: the slot of the node to which you were entangled before the swap

  • past_remote_slot::Int64: the slot of your node that we were entangled to

  • new_remote_node::Int64: the id of the node to which you are now entangled after the swap

  • new_remote_slot::Int64: the slot of the node to which you are now entangled after the swap

  • correction::Int64: what Pauli correction you need to perform

source
QuantumSavory.ProtocolZoo.EntanglerProtType
struct EntanglerProt <: QuantumSavory.ProtocolZoo.AbstractProtocol

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.

  • sim::ConcurrentSim.Simulation: time-and-schedule-tracking instance from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • nodeA::Int64: the vertex index of node A

  • nodeB::Int64: the vertex index of node B

  • pairstate::SymQObj: the state being generated (supports symbolic, numeric, noisy, and pure)

  • success_prob::Float64: success probability of one attempt of entanglement generation

  • attempt_time::Float64: duration of single entanglement attempt

  • local_busy_time_pre::Float64: fixed "busy time" duration immediately before starting entanglement generation attempts

  • local_busy_time_post::Float64: fixed "busy time" duration immediately after the a successful entanglement generation attempt

  • retry_lock_time::Union{Nothing, Float64}: how long to wait before retrying to lock qubits if no qubits are available (nothing for queuing up)

  • rounds::Int64: how many rounds of this protocol to run (-1 for infinite)

  • attempts::Int64: maximum number of attempts to make per round (-1 for infinite)

  • chooseslotA::Union{Int64, Function}: function Int->Bool or an integer slot number, specifying the slot to take among available free slots in node A

  • chooseslotB::Union{Int64, Function}: function Int->Bool or an integer slot number, specifying the slot to take among available free slots in node B

  • randomize::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 slots

  • uselock::Bool: whether the protocol should look for unlocked slots to entangle and lock them during the protocol

  • margin::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: Like margin, but it is enforced even when no entanglement has been established yet. Usually smaller than margin.

  • 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 form tag(remote_node, remote_slot), by default EntanglementCounterpart.

source
QuantumSavory.ProtocolZoo.SwapperProtType
struct SwapperProt <: QuantumSavory.ProtocolZoo.AbstractProtocol

A 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 from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • node::Int64: the vertex of the node where swapping is happening

  • chooseslots::Union{Function, Vector{Int64}}: function Int->Bool or a vector of allowed slot indices, specifying the slots to take among swappable slots in the node

  • nodeL::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 node

  • nodeH::Union{QuantumSavory.Wildcard, Int64, Function}: the vertex of the other remote node for the swap, the "high" counterpart of nodeL; if you are working on a repeater chain, a good choice is >(current_node), i.e. any node to the "right" of the current node

  • chooseL::Function: the nodeL predicate can return many positive candidates; chooseL picks one of them (by index into the array of filtered nodeL results), defaults to a random pick arr->rand(keys(arr)); if you are working on a repeater chain a good choice is argmin, i.e. the node furthest to the "left"

  • chooseH::Function: the nodeH counterpart for chooseH; if you are working on a repeater chain a good choice is argmax, i.e. the node furthest to the "right"

  • local_busy_time::Float64: fixed "busy time" duration immediately before starting entanglement generation attempts

  • retry_lock_time::Union{Nothing, Float64}: how long to wait before retrying to lock qubits if no qubits are available (nothing for queuing up and waiting)

  • rounds::Int64: how many rounds of this protocol to run (-1 for 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) (nothing for no limit) – you probably want to use CutoffProt if you have an agelimit

source
QuantumSavory.ProtocolZoo.Switches.SimpleSwitchDiscreteProtType
struct SimpleSwitchDiscreteProt <: QuantumSavory.ProtocolZoo.AbstractProtocol

A 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 from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • switchnode::Int64: the vertex index of the switch

  • clientnodes::Vector{Int64}: the vertex indices of the clients

  • success_probs::Vector{Float64}: best-guess about success of establishing raw entanglement between client and switch

  • ticktock::Float64: duration of a single full cycle of the switching decision algorithm

  • rounds::Int64: how many rounds of this protocol to run (-1 for infinite)

  • assignment_algorithm::Function: the algorithm to use for memory slot assignment, defaulting to promponas_bruteforce_choice

  • _backlog::QuantumSavory.ProtocolZoo.Switches.SymMatrix{Matrix{Int64}}

source
QuantumSavory.ProtocolZoo.Switches.SwitchRequestType
struct SwitchRequest

Notify a switch that you request to be entangled with another node.

  • requester::Int64: the id of the node making the request

  • remote_node::Int64: the id of the remote node to which we want to be entangled

source
QuantumSavory.ProtocolZoo.QTCP.EndNodeControllerType
struct EndNodeController <: QuantumSavory.ProtocolZoo.AbstractProtocol
  • sim: time-and-schedule-tracking instance from ConcurrentSim

  • net: a network graph of registers

  • node: the vertex index of where the protocol is located

Managing the following transformations of classical control signals:

  • Flow @ start node → sequence of QDatagram @ start node
  • QDatagramSuccess received from destination node → continuing the flow's sequence, reevaluating window
    • and converting LinkLevelReplyAtSource to QTCPPairBegin
  • QDatagram @ destination node → QDatagramSuccess sent to start node
    • and converting LinkLevelReplyAtHop to QTCPPairEnd
source
QuantumSavory.ProtocolZoo.QTCP.FlowType
struct Flow
  • src::Int64: who initiates the request and also initiates the qdatagrams

  • dst::Int64: the destination node

  • npairs::Int64: the number of requested pairs

  • uuid::Int64: the unique id of the flow

source
QuantumSavory.ProtocolZoo.QTCP.LinkControllerType
struct LinkController <: QuantumSavory.ProtocolZoo.AbstractProtocol
  • sim: time-and-schedule-tracking instance from ConcurrentSim

  • net: a network graph of registers

  • nodeA: 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:

  • LinkLevelRequestLinkLevelReply and LinkLevelReplyAtHop
  • this is the entity that actually establishes the link-level entanglement
source
QuantumSavory.ProtocolZoo.QTCP.LinkLevelReplyType
struct LinkLevelReply
  • flow_uuid::Int64: the uuid of the flow we are providing entanglement for

  • seq_num::Int64: sequence number of the qdataframe we are providing entanglement for

  • memory_slot::Int64: where is the entangled qubit stored

source
QuantumSavory.ProtocolZoo.QTCP.LinkLevelReplyAtHopType
struct LinkLevelReplyAtHop
  • flow_uuid::Int64: the uuid of the flow we are providing entanglement for

  • seq_num::Int64: sequence number of the qdataframe we are providing entanglement for

  • memory_slot::Int64: where is the entangled qubit stored

source
QuantumSavory.ProtocolZoo.QTCP.LinkLevelReplyAtSourceType
struct LinkLevelReplyAtSource
  • flow_uuid::Int64: the uuid of the flow we are providing entanglement for

  • seq_num::Int64: sequence number of the qdataframe we are providing entanglement for

  • memory_slot::Int64: where is the entangled qubit stored

source
QuantumSavory.ProtocolZoo.QTCP.LinkLevelRequestType
struct LinkLevelRequest
  • flow_uuid::Int64: the uuid of the flow we are providing entanglement for

  • seq_num::Int64: sequence number of the qdataframe we are providing entanglement for

  • remote_node::Int64: the remote node with which we want to establish entanglement

source
QuantumSavory.ProtocolZoo.QTCP.NetworkNodeControllerType
struct NetworkNodeController <: QuantumSavory.ProtocolZoo.AbstractProtocol
  • sim: time-and-schedule-tracking instance from ConcurrentSim

  • net: a network graph of registers

  • node: the vertex index of where the protocol is located

Managing the following transformations of classical control signals:

  • QDatagramLinkLevelRequest to link with next hop (according to a pathfinding algorithm)
  • LinkLevelReplyQDatagram forwarded to next hop and
    • either a swap with the pre-existing LinkLevelReplyAtHop
    • or just tagging LinkLevelReplyAtSource if we are at the source node of the flow
  • this is the entity that actually:
    • performs pathfinding
    • performs entanglement swapping
source
QuantumSavory.ProtocolZoo.QTCP.QDatagramType
struct QDatagram
  • flow_uuid::Int64: the uuid of the flow we are generated for

  • flow_src::Int64: who initiates the flow request and also initiates the qdatagrams

  • flow_dst::Int64: the destination node for the flow

  • correction::Int64: the Pauli frame correction

  • seq_num::Int64: sequence number of the qdataframe in the given flow

  • start_time::Float64: original start time of the qdatagram

source
QuantumSavory.ProtocolZoo.QTCP.QTCPPairBeginType
struct QTCPPairBegin
  • flow_uuid::Int64: the uuid of the flow we are generated for

  • flow_src::Int64: who initiates the flow request and also initiates the qdatagrams

  • flow_dst::Int64: the destination node for the flow

  • seq_num::Int64: sequence number of the qdataframe in the given flow

  • memory_slot::Int64: the memory slot of the entangled qubit

  • start_time::Float64: original start time of the qdatagram

source
QuantumSavory.ProtocolZoo.QTCP.QTCPPairEndType
struct QTCPPairEnd
  • flow_uuid::Int64: the uuid of the flow we are generated for

  • flow_src::Int64: who initiates the flow request and also initiates the qdatagrams

  • flow_dst::Int64: the destination node for the flow

  • seq_num::Int64: sequence number of the qdataframe in the given flow

  • memory_slot::Int64: the memory slot of the entangled qubit

  • start_time::Float64: original start time of the qdatagram

source
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.GraphStateConstructorType
struct GraphStateConstructor <: QuantumSavory.ProtocolZoo.AbstractProtocol

A 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 from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • graph::Graphs.AbstractGraph: the graph state to be constructed, with vertices corresponding to entries in nodes

  • nodes::Vector{Int64}: nodes at which the graph state is distributed

  • communication_slot::Int64: slot for entanglement generation (e.g. electron spin)

  • storage_slot::Int64: slot for storage (e.g. nuclear spin)

source
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.GraphToResourceType
struct GraphToResource <: QuantumSavory.ProtocolZoo.AbstractProtocol

Apply 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 from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • nodes::Vector{Int64}: nodes at which the graph state is distributed

  • slot::Int64: slot at each node where the graph state qubit is stored

  • hadamard_idx::Vector{Int64}: indices where Hadamard corrections are to be applied

  • iphase_idx::Vector{Int64}: indices where inverse Phase corrections are to be applied

  • flips_idx::Vector{Int64}: indices where Z corrections are to be applied

source
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.PurifierBellMeasurementsType
struct PurifierBellMeasurements <: QuantumSavory.ProtocolZoo.AbstractProtocol

Apply 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 from ConcurrentSim

  • net::RegisterNet: a network graph of registers

  • nodes::Vector{Int64}: nodes at which the Bell measurements will be happening

  • local_chief_idx::Int64: "Chief" node for our local set of nodes, the source of the bitpacked message

  • remote_chief_idx::Int64: "Chief" node for the remote set of nodes, the destination node for the bitpacked message

  • x_slot::Int64: slot on which the X measurement is performed (same for all nodes), the control of the CNOT

  • z_slot::Int64: slot on which the Z measurement is performed (same for all nodes), the target of the CNOT

source
QuantumSavory.ProtocolZoo.MBQCEntanglementDistillation.graph_builderMethod
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───2

Prepare 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
true

Importantly, 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
source