Discrete Event Simulator
Quantum network protocols are usually not "apply these gates in order and stop." They wait for messages, reserve resources, react to timeouts, and run in parallel with other control loops. That is why QuantumSavory uses discrete-event simulation for protocol logic.
The two main building blocks are:
@resumablefromResumableFunctions.jl, which defines a coroutine-like process that can suspend and resume;@processfromConcurrentSim.jl, which schedules that process on the simulation clock.
Why Discrete Event Simulation Fits LOCC
LOCC protocols are driven by events:
- a Bell pair becomes available,
- a classical message arrives,
- a timeout expires,
- or a slot lock is released.
Those events do not happen at the same time, and several protocol components may be active at once. Discrete-event execution lets each protocol wait only on the events it cares about.
@resumable Processes
A @resumable function can @yield a condition and continue only when that condition is satisfied.
@resumable function swapper(net, node)
mb = messagebuffer(net, node)
msg = @yield querydelete_wait!(mb, :swap_request)
# local quantum work happens only after the message arrives
return msg
endThis style is useful because the protocol code reads in the same order as the logical protocol: wait, receive, act.
@process Starts A Running Simulation Process
Defining a resumable function is not enough. To make it participate in the simulation, schedule it with @process.
sim = Simulation()
@process swapper(net, 2)Once scheduled, the process runs whenever the events it is waiting on become ready.
AbstractProtocol Is The Reusable Protocol Pattern
Plain @resumable functions are supported, but QuantumSavory uses a more structured convention for reusable protocols: a callable struct subtype of AbstractProtocol.
struct MySwapperProt <: AbstractProtocol
sim::Simulation
net::RegisterNet
node::Int
alice::Int
charlie::Int
end
@resumable function (prot::MySwapperProt)()
(; sim, net, node, alice, charlie) = prot
mb = messagebuffer(net, node)
@yield querydelete_wait!(mb, :swap_request)
a = query(net[node], EntanglementCounterpart, alice, ❓)
b = query(net[node], EntanglementCounterpart, charlie, ❓)
@yield lock(a.slot) & lock(b.slot)
# strictly speaking, we should be checking that
# by the time the locks are acquired
# alice and charlie still have the properties we have queried for
# (someone else might have consumed the entanglement in between)
x, y = LocalEntanglementSwap()(a.slot, b.slot)
unlock(a.slot)
unlock(b.slot)
return x, y
endThis pattern is useful because configuration and runtime context stay packaged with the protocol. It is easier to pass around, store, visualize, and reuse than a large free function with many arguments.
Common Wait Conditions
The most important wait sources are:
timeout(sim, delay)for waiting a fixed amount of simulated time;onchange(register)oronchange(register, Tag)for register changes;onchange(messagebuffer)for incoming classical messages;query_wait(...)for wait-until-query-succeeds;querydelete_wait!(...)for wait-until-query-succeeds-and-consume;lock(regref)for resource acquisition.
The query_wait helpers are especially useful because they combine the common "wait for change, query again, repeat" pattern into one call. query_wait on its own does not lock a register or its tags – if the protocol will consume a tag, use querydelete_wait! or re-query after acquiring locks.
Condition Combinators
Wait conditions can be combined directly.
@yield lock(q1) & lock(q2)
@yield onchange(mb) | timeout(sim, 10.0)& means all conditions must become ready. | means any one of them is enough to resume the process.
This is what makes concurrent control logic concise. A protocol can express "wait until both resources are free" or "wait for a message, but only up to a deadline" without manually writing an event loop.
Locks Are Part Of Protocol Logic
Protocols often compete for the same memory slot or communication qubit. QuantumSavory therefore treats locks as ordinary waitable conditions inside the same event system.
That matters because concurrency control is not a separate concern in a network simulation. It directly changes which quantum resources are available and when.
Where The Metadata Plane Fits
Discrete-event execution and the metadata plane are designed to work together. Protocols usually wait on metadata changes or message-buffer queries, then act on the matching quantum resources.
That is why the protocol code can stay modular: one component publishes a fact, another waits for it, and neither needs an explicit handle to the other's internal state machine.
Where To Go Next
- Read Metadata and Protocol Composition for the control plane built on top of these waits.
- Read Tag and Query API for the concrete query and wait functions.
- Read ProtocolZoo API for reusable protocols built in this style.