![]() |
Q2NS dev
ns-3 module
|
This document describes the main structure of Q2NS and how its components fit together. It is intended as a conceptual guide covering the main points, not as a full API reference. For the detailed interface of each class, see the Doxygen documentation.
Q2NS is built around a modular architecture with a deliberate separation between network control and in-network operations. This reflects one of the central design principles described in the Q2NS paper: a quantum network simulator should separate orchestration logic from node-local and link-local execution while keeping the modules composable and replaceable.
At a high level, Q2NS revolves around three central ideas:
NetController builds and configures the network. Internally, this owns the QStateRegistry, which is the shared source of truth for backend states, qubit membership, and qubit locations.QNodes and QChannels are the main network entities. They can process, transmit, and receive quantum information. QNodes can also engage with the classical networking stack since they derive from ns3::Node. Internally, QNodes delegate local quantum processing to a QProcessor and quantum networking to a QNetworker. QChannels can be configured with various QMaps to simulate noise, loss, and more. They are connected to QNodes with QNetDevices, but the current API never requires users to create or interact with these for convenience.Qubits are the main handles for quantum information that can be operated on and transmitted, connected internally to QStates through the QStateRegistry. QState is an interface with three different implementations or "backends": statevector (q2ns::QStateBackend::Ket), density matrix (q2ns::QStateBackend::DM), and stabilizer (q2ns::QStateBackend::Stab). Generally the user does not interact with QStates directly, unless they want to analyze various state characteristics with the q2ns::analysis methods (e.g., q2ns::analysis::Fidelity(...), q2ns::analysis::Purity(...), etc.).NetController is the top-level orchestration object. Creating this object is where all Q2NS simulations start.
Its responsibilities are:
QStateRegistryQNode objectsQChannel and QNetDevice links between nodes or in simple topologies such as chains and all-to-all networksUsers typically use these methods:
SetQStateBackend() / GetQStateBackend()CreateNode() / GetNode()InstallQuantumLink() / InstallQuantumChain() / InstallQuantumAllToAll()GetChannel()GetState()QStateRegistry is the central bookkeeping layer of Q2NS.
It is the shared source of truth for:
This centralization is an important aspect of quantum networking. Qubits move between nodes and channels, and backend states may be split or merged after measurement and multi-qubit gates. By managing that bookkeeping in one place, Q2NS avoids scattering state-tracking logic across multiple components.
QNode is the main user-facing per-node API. It is created with NetController::CreateNode().
Its responsibilities are:
QProcessor)QNetworker)Users typically use these methods:
CreateQubit()CreateBellPair()Apply()Measure()MeasureBell()Send()SetRecvCallback()QProcessor is an internal helper object owned by QNode that acts as the local quantum execution engine of a node.
Its responsibilities are:
QStateRegistryQNetworker is another internal helper owned by QNode that acts as the node-local networking front end for qubit transmission.
Its responsibilities are:
QNetDevices)QMapInstance that was determined by the QChannel's QMapQNetDevice is a thin bridge between QNetworker and QChannel.
It is intentionally minimal, but exists in parallel with the classical ns-3 NetDevice. It forwards outgoing qubits to the attached channel and forwards arriving qubits upward to the bound QNetworker.
This keeps the send and receive path compatible with the usual ns-3 approach of nodes, devices, and channels, while considering that quantum devices are not full classical packet devices that engage with ports and IP addresses.
QChannel models a duplex quantum channel between two endpoints.
Users typically configure a QChannel using SetAttribute() (part of the ns-3 attribute system), specifically for:
"Delay" (symmetric) / "DelayAB" / "DelayBA""Jitter" (symmetric) / "JitterAB" / "JitterBA""QMap" (symmetric) / "QMapAB" / "QMapBA"On each transmission, QChannel:
QMapQMapInstanceA notable, but subtle design choice is that the sampled QMapInstance is not applied inside the channel immediately. Instead, it is carried with the transmitted qubit and executed later at the receiver after the qubit has become local to the destination node in order to use that node's quantum processing capabilities. This occurs before any receive callback or other possible actions and therefore accurately represents the cumulative effect of the channel transmission.
QMap models transmission-induced effects and produces a random QMapInstance based on this model for each transmission.
Examples include:
A QMap does not directly mutate a qubit when a channel is configured. Instead, the QChannel samples a QMapInstance for each transmission. This callable is run at the receiving node after the qubit has been adopted locally.
Qubit is a lightweight handle object. It does not own the underlying quantum state or anything other than several forms of identification:
Actual state resolution and location tracking happen through QStateRegistry. Most user-facing operations (gates, measurements, transmission, state inspection, etc.) are intentionally provided on QNode, not on Qubit itself.
QState is the backend-agnostic quantum-state plug-in interface.
Concrete implementations, "backends", currently include:
QStateKet (referrable through the enum value QStateBackend::Ket)QStateDM (referrable through the enum value QStateBackend::DM)QStateStab (referrable through the enum value QStateBackend::Stab)These provide different tradeoffs:
QStateKet stores a pure state ket and supports general unitary evolutionQStateDM stores a density matrix and supports mixed-state evolutionQStateStab stores a stabilizer state and supports efficient Clifford-state simulationFrom the user perspective, the backend can be easily set using NetController::SetQStateBackend() without needing to change any other part of their code, other than verifying that it is simulatable by the given backend (notably that non-Clifford gates are used with QStateStab, which will throw an error).
q2ns::analysis is a namespace containing a number of quantum state analysis functions.
Examples include:
A typical simulation proceeds as follows:
NetController.QNode objects through the controller.QNode.The send path is:
QNode::Send()QNode delegates to QNetworker::Send()QNetworker selects an outgoing QNetDeviceQNetDevice forwards to QChannel::SendFrom()QChannel samples delay, jitter, and a QMapInstanceThe receive path is:
QNetDeviceQNetDevice forwards upward to QNetworker::ReceiveFromDevice()QMapInstance is appliedQMapInstance), the registered receive callback is invokedThe measurement path is:
QNode::Measure()QNode delegates to QProcessor::Measure()QProcessor retrieves the qubit's location and QState from the QStateRegistry and verifies if the qubit is local to this node before proceedingQProcessor calls QState::Measure(), receiving a measured 1-qubit state, a survivor state for the remaining qubits, and the classical outcome bitQProcessor then rebinds qubit handles through QStateRegistry with the two new QStates and returns the classical outcome bitNotably, every measurement results in a new single qubit state. If the qubit was originally in a multi-qubit state, this will be split and the measurement path will effectively produce one more QState object in the QStateRegistry than when it started. This helps keep the size of any one state small, thereby reducing the runtime and memory complexity.
The gate application path is:
QNode::Apply()QNode delegates to QProcessor::Apply()QProcessor retrieves the qubit's location from the QStateRegistry and verifies if the qubit is local to this node before proceedingQProcessor calls QStateRegistry::MergeStates() to produce a single merged (tensor product) QState for all qubits involved in this gate applicationQProcessor retrieves the qubits' QState from the QStateRegistryQprocessor calls QState::Apply() and returns trueNotably, every multi-qubit gate results in a single, merged state. If the qubits were not already connected to a single QState object, they will be in the end–even if they are not necessarily entangled. This means multi-qubit gate application can increase the runtime and memory complexity.
[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.