![]() |
Q2NS dev
ns-3 module
|
This tutorial walks through writing simulations using Q2NS. We focus on practical usage rather than deep theory. By the end you will know how to:
If you want to watch simulations run before writing code, see Tutorial 0 (the Q2NSViz visualizer).
Q2NS extends ns-3 with quantum networking primitives.
The key objects you interact with are:
| Object | Purpose |
|---|---|
NetController | Global orchestrator for the quantum network |
QNode | A node capable of storing and processing qubits |
Qubit | A logical quantum bit |
QState | The underlying quantum state backend |
The NetController creates nodes and links, while QNode objects perform quantum operations locally and send/receive qubits to other QNode objects.
A simulation typically looks like this:
NetController and classical primitivesSimulator::Schedule(...)Simulator::Run()Q2NS is a discrete event simulator! Simulation events are scheduled in a queue and executed sequentially. All Q2NS examples ultimately run inside the normal ns-3 scheduler. Most code you write will involve scheduling events with:
And, in the end, we run the simulation with:
During Run(), all of these can happen together:
This is important: most interesting protocols are not a straight-line program. You usually create nodes and links first, then schedule actions, then let ns-3 execute the timeline. Some of these actions may schedule other actions themselves. All time in ns-3 is simulation time, not wall-clock time. A simulation that runs for 10 simulated seconds may execute much faster (or slower) in real time depending on the complexity of the events being simulated.
Scheduling events using Simulator::Schedule requires specifying the time and a callable. The [A, q]() syntax is a C++ lambda – a function defined inline that captures local variables so they remain accessible when the event fires later:
We begin with the simplest possible program: a single node manipulating and measuring a qubit.
Here we:
The first step is to include the needed headers and namespaces:
The randomness of quantum measurements is governed by two knobs, more details are given in randomness.md.
The NetController is the orchestrator of the simulation. The backend must be configured before creating qubits:
Q2NS supports three quantum state representations, each with different trade-offs:
| Backend | Represents | Best for | Limitations |
|---|---|---|---|
Ket | Pure-state statevector | Exact simulation, small qubit counts | Exponential memory in qubit count; pure states only |
DM | Density matrix (CPTP) | Mixed states, open systems, noise | Exponential memory in qubit count |
Stab | Stabilizer tableau | Large Clifford circuits, scalable | Clifford group only – T gate and arbitrary unitaries are not supported |
A QState handle can be obtained from the NetController at any time. Note that GetStateId() is a registry-internal identifier assigned to the state object, not the qubit itself – a state may represent multiple entangled qubits jointly:
Gates and measurements are applied through the node. Since the simulation is event-driven they are wrapped in lambdas and scheduled at specific simulation times. Apply takes a gate and a list of target qubits; Measure collapses the qubit and returns the classical outcome (0 or 1):
By putting all together, we have the final simulation file
The key resource of the Quantum Internet is entanglement. Here we:
QNodes connected by a quantum linkWe begin by creating two nodes and connecting them with a quantum link:
This creates a bidirectional channel with a 10 ns propagation delay. The delay can be configured independently for each direction with ch->SetDelayAB(...) and ch->SetDelayBA(...).
We then create a Bell pair at node A:
This is equivalent to:
which prepares the maximally entangled state:
$$|\Phi^+\rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}$$
A callback is a function you register in advance that the simulator will call for you when a specific event occurs. Here we register a receive callback at B before calling Send – this is important because qubit delivery is asynchronous: Send returns immediately and the qubit arrives at B only after the channel delay has elapsed. If no callback is registered, the qubit still arrives but nothing happens with it.
SetRecvCallback fires only when a qubit arrives; it does not trigger on classical packets. The [&] capture passes all local variables by reference into the lambda:
Since Send is non-blocking and delivery is asynchronous, measurements must be scheduled after the channel delay has elapsed. Here we use a 20 ns budget (1 ns send time + 10 ns delay + margin), capturing the result in a lambda:
By putting all together, we have the final simulation file
From the ns-3 root:
Run the introductory examples to see the above concepts in action:
Create a single node, allocate a qubit, apply an X gate (bit-flip), then measure in the Z-basis. Verify the result is always 1. Then try applying a Z gate to ∣0⟩ and confirm the measurement is always 0: since Z is diagonal in the computational basis (Z∣0⟩ = ∣0⟩, Z∣1⟩ = -∣1⟩), it does not alter the Born-rule probabilities of any Z-basis measurement.
[1] Quantum Internet Architecture: Unlocking Quantum-Native Routing via Quantum Addressing (invited paper). Marcello Caleffi and Angela Sara Cacciapuoti – in IEEE Transactions on Communications, vol. 74, pp. 3577–3599, 2026.
[2] An Extensible Quantum Network Simulator Built on ns-3: Q2NS Design and Evaluation. Adam Pearson, Francesco Mazza, Marcello Caleffi, Angela Sara Cacciapuoti – Computer Networks (Elsevier) 2026.
[3] Q2NS: A Modular Framework for Quantum Network Simulation in ns-3 (invited paper). Adam Pearson, Francesco Mazza, Marcello Caleffi, Angela Sara Cacciapuoti – Proc. QCNC 2026.
[4] Q2NS Demo: a Quantum Network Simulator based on ns-3. Francesco Mazza, Adam Pearson, Marcello Caleffi, Angela Sara Cacciapuoti – 2026.
This work has been funded by the European Union under Horizon Europe ERC-CoG grant QNattyNet, n. 101169850. Views and opinions expressed are those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Council Executive Agency. Neither the European Union nor the granting authority can be held responsible for them.