Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2nsviz-entanglement-distribution-example.cc
Go to the documentation of this file.
1/*-----------------------------------------------------------------------------
2 * Q2NS - Quantum Network Simulator
3 * Copyright (c) 2026 quantuminternet.it
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *---------------------------------------------------------------------------*/
9/**
10 * @file q2nsviz-entanglement-distribution-example.cc
11 * @brief Pairwise Bell-pair distribution across 3 fully-connected nodes.
12 *
13 * Three nodes (Node1, Node2, Node3) share quantum and classical links in a
14 * full-mesh topology. Bell pairs are distributed sequentially so that every
15 * pair of nodes ends up sharing one entangled qubit:
16 *
17 * Step 1 - Node1 creates Bell pair 0, keeps half, sends the other to Node2.
18 * Step 2 - Node1 creates Bell pair 1, keeps half, sends the other to Node3.
19 * Step 3 - Node2 creates Bell pair 2, keeps half, sends the other to Node3.
20 *
21 * After the protocol each link (1-2, 1-3, 2-3) carries one shared Bell pair.
22 *
23 * Timing model (illustrative):
24 * kSingleGate = 100 ns (single-qubit gate)
25 * kTwoQGate = 300 ns (two-qubit gate)
26 * kQDelay = 100 ns (quantum channel propagation, ~20 m fiber)
27 *
28 * Steps start at T = 1, 3, 5 us for clear visualization; final trace at ~20 us.
29 *
30 * Visualization output is written to
31 * examples/example_traces/q2nsviz-entanglement-distribution-example.json and can be loaded
32 * in the q2nsviz viewer (src/q2ns/utils/q2nsviz-serve.sh).
33 *
34 * See docs/tutorials/tutorial-00.md for a detailed walkthrough.
35 */
36
37#include "ns3/core-module.h"
38#include "ns3/internet-module.h"
39#include "ns3/network-module.h"
40#include "ns3/point-to-point-module.h"
41#include "ns3/simulator.h"
42
43#include "ns3/q2ns-netcontroller.h"
44#include "ns3/q2ns-qgate.h"
45#include "ns3/q2ns-qnode.h"
46#include "ns3/q2ns-qubit.h"
47
48#include "ns3/q2nsviz-trace-writer.h"
49#include "ns3/q2nsviz-trace.h"
50
51#include <array>
52#include <iostream>
53
54using namespace ns3;
55using namespace q2ns;
56
57static const uint16_t kAckPort = 9100;
58
59int main(int argc, char** argv) {
60 std::cout << "[DEMO] Entanglement distribution (3 nodes) starting\n";
61
62 RngSeedManager::SetSeed(1);
63 RngSeedManager::SetRun(1);
64
65 CommandLine cmd;
66 cmd.Parse(argc, argv);
67
69 "examples/example_traces/q2nsviz-entanglement-distribution-example.json");
70
71 Trace("Entanglement Distribution with 3 nodes");
72
73 Time::SetResolution(Time::NS);
74
75 NetController net;
76 net.SetQStateBackend(QStateBackend::Stab);
77
78 // --- Nodes ---
79 Ptr<QNode> node1 = net.CreateNode();
80 Ptr<QNode> node2 = net.CreateNode();
81 Ptr<QNode> node3 = net.CreateNode();
82
83 TraceCreateNode("Node1", 85, 50);
84 TraceCreateNode("Node2", 32, 80);
85 TraceCreateNode("Node3", 32, 19);
86
87 // --- Timing constants ---
88 // Values are illustrative.
89 const Time kSingleGate = NanoSeconds(100); // single-qubit gate
90 const Time kTwoQGate = NanoSeconds(300); // two-qubit gate
91 const Time kQDelay = NanoSeconds(100); // quantum link propagation (~20 m fiber)
92 const Time kClassical = kQDelay; // classical propagation = quantum (~20 m fiber)
93
94 // --- Quantum links (full mesh) ---
95 auto ch12 = net.InstallQuantumLink(node1, node2);
96 auto ch13 = net.InstallQuantumLink(node1, node3);
97 auto ch23 = net.InstallQuantumLink(node2, node3);
98 ch12->SetAttribute("Delay", TimeValue(kQDelay));
99 ch13->SetAttribute("Delay", TimeValue(kQDelay));
100 ch23->SetAttribute("Delay", TimeValue(kQDelay));
101
102 TraceCreateChannel("Node1", "Node2", "quantum");
103 TraceCreateChannel("Node1", "Node2", "classical");
104 TraceCreateChannel("Node1", "Node3", "quantum");
105 TraceCreateChannel("Node1", "Node3", "classical");
106 TraceCreateChannel("Node2", "Node3", "quantum");
107 TraceCreateChannel("Node2", "Node3", "classical");
108
109 // --- Classical network (full mesh p2p) ---
110 InternetStackHelper internet;
111 internet.Install(node1);
112 internet.Install(node2);
113 internet.Install(node3);
114
115 PointToPointHelper p2p;
116 p2p.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
117 p2p.SetChannelAttribute("Delay", StringValue("100ns"));
118
119 Ipv4AddressHelper ipv4;
120
121 ipv4.SetBase("10.1.1.0", "255.255.255.0");
122 auto ifs12 = ipv4.Assign(p2p.Install(node1, node2));
123
124 ipv4.SetBase("10.1.2.0", "255.255.255.0");
125 auto ifs13 = ipv4.Assign(p2p.Install(node1, node3));
126
127 ipv4.SetBase("10.1.3.0", "255.255.255.0");
128 auto ifs23 = ipv4.Assign(p2p.Install(node2, node3));
129
130 Ipv4GlobalRoutingHelper::PopulateRoutingTables();
131
132 // ---------------------------------------------------------------------------
133 // UDP sockets for classical ACKs
134 // Node2 -> Node1 (Bell pair 0 confirmed)
135 // Node3 -> Node1 (Bell pair 1 confirmed)
136 // Node3 -> Node2 (Bell pair 2 confirmed)
137 // ---------------------------------------------------------------------------
138
139 // Node1 ACK-receive socket (receives ACKs from Node2 and Node3)
140 auto node1AckRx = Socket::CreateSocket(node1, UdpSocketFactory::GetTypeId());
141 node1AckRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kAckPort));
142 int node1AcksReceived = 0;
143 node1AckRx->SetRecvCallback([&](Ptr<Socket> sock) {
144 Address from;
145 while (sock->RecvFrom(from)) {
146 ++node1AcksReceived;
147 TraceNodeText("Node1", StrCat("ACK received (", node1AcksReceived, "/2 links confirmed)"));
148 std::cout << "[NODE1] ACK received (" << node1AcksReceived << "/2)\n";
149 if (node1AcksReceived == 2)
150 Trace("Node1: both Bell pair links confirmed");
151 }
152 });
153
154 // Node2 ACK-receive socket (receives ACK from Node3 for Bell pair 2)
155 auto node2AckRx = Socket::CreateSocket(node2, UdpSocketFactory::GetTypeId());
156 node2AckRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kAckPort));
157 node2AckRx->SetRecvCallback([&](Ptr<Socket> sock) {
158 Address from;
159 while (sock->RecvFrom(from)) {
160 TraceNodeText("Node2", "ACK from Node3: Bell pair 2 link confirmed");
161 Trace("Node2: Bell pair 2 confirmed by Node3");
162 std::cout << "[NODE2] ACK from Node3 received\n";
163 }
164 });
165
166 // Pre-created transmit sockets: addresses from interface containers.
167 // ifs12.GetAddress(0) = Node1 on the 1-2 subnet, etc.
168 auto node2TxSock = Socket::CreateSocket(node2, UdpSocketFactory::GetTypeId());
169 node2TxSock->Connect(InetSocketAddress(ifs12.GetAddress(0), kAckPort));
170
171 auto node3ToNode1Sock = Socket::CreateSocket(node3, UdpSocketFactory::GetTypeId());
172 node3ToNode1Sock->Connect(InetSocketAddress(ifs13.GetAddress(0), kAckPort));
173
174 auto node3ToNode2Sock = Socket::CreateSocket(node3, UdpSocketFactory::GetTypeId());
175 node3ToNode2Sock->Connect(InetSocketAddress(ifs23.GetAddress(0), kAckPort));
176
177 // Node2 qubit-receive callback (Bell pair 0: q0b arrives from Node1)
178 node2->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
179 TraceSetBitColor(q->GetLabel(), "#90EE90");
180 TraceNodeText("Node2", StrCat("Qubit ", q->GetLabel(), " arrived - entangled with Node1"));
181 std::cout << "[NODE2] Qubit " << q->GetLabel() << " arrived\n";
182 node2TxSock->Send(Create<Packet>(1));
183 TraceSendPacket("Node2", "Node1", Simulator::Now(), Simulator::Now() + kClassical,
184 StrCat("ACK: ", q->GetLabel(), " received"));
185 });
186
187 // Node3 qubit-receive callback (Bell pairs 1 and 2 arrive)
188 // bell_1_b comes from Node1; bell_2_b comes from Node2.
189 int node3QubitCount = 0;
190 node3->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
191 ++node3QubitCount;
192 TraceSetBitColor(q->GetLabel(), "#90EE90");
193 TraceNodeText("Node3", StrCat("Qubit ", q->GetLabel(), " arrived (", node3QubitCount, "/2)"));
194 std::cout << "[NODE3] Qubit " << q->GetLabel() << " arrived\n";
195 if (node3QubitCount == 1) {
196 node3ToNode1Sock->Send(Create<Packet>(1));
197 TraceSendPacket("Node3", "Node1", Simulator::Now(), Simulator::Now() + kClassical,
198 StrCat("ACK: ", q->GetLabel(), " received"));
199 } else {
200 node3ToNode2Sock->Send(Create<Packet>(1));
201 TraceSendPacket("Node3", "Node2", Simulator::Now(), Simulator::Now() + kClassical,
202 StrCat("ACK: ", q->GetLabel(), " received"));
203 }
204 });
205
206 // --- Bell pair 0: Node1 <-> Node2, base T = 1 us ---
207 Simulator::Schedule(MicroSeconds(1), [&] {
208 Trace("Step 1: Node1 initializes Bell pair 0 (for Node2)");
209 TraceNodeText("Node1", "Step 1: init Bell pair 0");
210
211 auto q0a = node1->CreateQubit();
212 q0a->SetLabel("bell_0_a");
213 auto q0b = node1->CreateQubit();
214 q0b->SetLabel("bell_0_b");
215
216 TraceCreateBit("Node1", "bell_0_a", "quantum", "#88CCEE");
217 TraceCreateBit("Node1", "bell_0_b", "quantum", "#88CCEE");
218
219 // T + 100 ns: H(q0a)
220 Simulator::Schedule(kSingleGate, [=]() {
221 node1->Apply(gates::H(), {q0a});
222 TraceSetBitColor("bell_0_a", "#BC71EB");
223 TraceNodeText("Node1", "H(bell_0_a)");
224
225 // T + 400 ns: CNOT(q0a, q0b) -> |Phi+>
226 Simulator::Schedule(kTwoQGate, [=]() {
227 node1->Apply(gates::CNOT(), {q0a, q0b});
228 TraceEntangle({"bell_0_a", "bell_0_b"}, kTwoQGate);
229 TraceNodeText("Node1", "Bell pair 0 ready");
230
231 // T + 500 ns: send q0b to Node2 (arrives T + 600 ns; ACK returns ~T + 1.6 ms)
232 Simulator::Schedule(kSingleGate, [=]() {
233 auto t0 = Simulator::Now();
234 node1->Send(q0b, node2->GetId());
235 TraceSendBit("bell_0_b", "Node1", "Node2", "quantum", t0, t0 + kQDelay);
236 TraceNodeText("Node1", "bell_0_b in transit to Node2");
237 TraceSetBitColor("bell_0_a", "#90EE90");
238 std::cout << "[STEP 1] Bell pair 0 sent: Node1 -> Node2\n";
239 });
240 });
241 });
242 });
243
244 // --- Bell pair 1: Node1 <-> Node3 ---
245 // (Bell pair 0 finishes at ~1.65 us; 3 us gives clear temporal separation.)
246 Simulator::Schedule(MicroSeconds(3), [&] {
247 Trace("Step 2: Node1 initializes Bell pair 1 (for Node3)");
248 TraceNodeText("Node1", "Step 2: init Bell pair 1");
249
250 auto q1a = node1->CreateQubit();
251 q1a->SetLabel("bell_1_a");
252 auto q1b = node1->CreateQubit();
253 q1b->SetLabel("bell_1_b");
254
255 TraceCreateBit("Node1", "bell_1_a", "quantum", "#88CCEE");
256 TraceCreateBit("Node1", "bell_1_b", "quantum", "#88CCEE");
257
258 Simulator::Schedule(kSingleGate, [=]() {
259 node1->Apply(gates::H(), {q1a});
260 TraceSetBitColor("bell_1_a", "#BC71EB");
261 TraceNodeText("Node1", "H(bell_1_a)");
262
263 Simulator::Schedule(kTwoQGate, [=]() {
264 node1->Apply(gates::CNOT(), {q1a, q1b});
265 TraceEntangle({"bell_1_a", "bell_1_b"}, kTwoQGate);
266 TraceNodeText("Node1", "Bell pair 1 ready");
267
268 Simulator::Schedule(kSingleGate, [=]() {
269 auto t0 = Simulator::Now();
270 node1->Send(q1b, node3->GetId());
271 TraceSendBit("bell_1_b", "Node1", "Node3", "quantum", t0, t0 + kQDelay);
272 TraceNodeText("Node1", "bell_1_b in transit to Node3");
273 TraceSetBitColor("bell_1_a", "#90EE90");
274 std::cout << "[STEP 2] Bell pair 1 sent: Node1 -> Node3\n";
275 });
276 });
277 });
278 });
279
280 // --- Bell pair 2: Node2 <-> Node3 ---
281 Simulator::Schedule(MicroSeconds(5), [&] {
282 Trace("Step 3: Node2 initializes Bell pair 2 (for Node3)");
283 TraceNodeText("Node2", "Step 3: init Bell pair 2");
284
285 auto q2a = node2->CreateQubit();
286 q2a->SetLabel("bell_2_a");
287 auto q2b = node2->CreateQubit();
288 q2b->SetLabel("bell_2_b");
289
290 TraceCreateBit("Node2", "bell_2_a", "quantum", "#88CCEE");
291 TraceCreateBit("Node2", "bell_2_b", "quantum", "#88CCEE");
292
293 Simulator::Schedule(kSingleGate, [=]() {
294 node2->Apply(gates::H(), {q2a});
295 TraceSetBitColor("bell_2_a", "#BC71EB");
296 TraceNodeText("Node2", "H(bell_2_a)");
297
298 Simulator::Schedule(kTwoQGate, [=]() {
299 node2->Apply(gates::CNOT(), {q2a, q2b});
300 TraceEntangle({"bell_2_a", "bell_2_b"}, kTwoQGate);
301 TraceNodeText("Node2", "Bell pair 2 ready");
302
303 Simulator::Schedule(kSingleGate, [=]() {
304 auto t0 = Simulator::Now();
305 node2->Send(q2b, node3->GetId());
306 TraceSendBit("bell_2_b", "Node2", "Node3", "quantum", t0, t0 + kQDelay);
307 TraceNodeText("Node2", "bell_2_b in transit to Node3");
308 TraceSetBitColor("bell_2_a", "#90EE90");
309 std::cout << "[STEP 3] Bell pair 2 sent: Node2 -> Node3\n";
310 });
311 });
312 });
313 });
314
315 // --- Final trace at 20 us (all ACKs have arrived by ~16 us) ---
316 Simulator::Schedule(MicroSeconds(20), [&] {
317 Trace("All Bell pairs distributed and confirmed - full-mesh entanglement established");
318 std::cout << "[DONE] All Bell pairs distributed\n";
319 });
320
321 Simulator::Stop(MilliSeconds(1));
322 Simulator::Run();
323 Simulator::Destroy();
324
326 std::cout << "[DONE] Entanglement distribution finished\n";
327 return 0;
328}
static TraceWriter & Instance()
void Open(const std::string &path)
Main user-facing facade for creating and configuring a quantum network.
ns3::Ptr< QNode > CreateNode(const std::string &label="")
Create a QNode with an optional human-readable label.
ns3::Ptr< QChannel > InstallQuantumLink(ns3::Ptr< QNode > a, ns3::Ptr< QNode > b)
Install a duplex quantum link between two nodes.
void SetQStateBackend(QStateBackend b)
Set the default backend used for newly created quantum states.
QGate H(ns3::Time d=ns3::Seconds(0))
Return the Hadamard gate descriptor.
Definition q2ns-qgate.h:383
QGate CNOT(ns3::Time d=ns3::Seconds(0))
Return the CNOT gate descriptor.
Definition q2ns-qgate.h:410
int main()
static const uint16_t kAckPort
std::string StrCat(Ts &&... parts)
void Trace(const std::string &text)
void TraceNodeText(const std::string &node, const std::string &text)
void TraceEntangle(const std::vector< std::string > &bits)
void TraceSendPacket(const std::string &from, const std::string &to, uint64_t t0_ns, uint64_t t1_ns, const std::string &label)
void TraceCreateBit(const std::string &node, const std::string &bitLabel, const std::string &kind, const std::string &color)
void TraceCreateNode(const std::string &label, int xPct, int yPct)
void TraceCreateChannel(const std::string &from, const std::string &to, const std::string &kind)
void TraceSendBit(const std::string &bitLabel, const std::string &from, const std::string &to, const std::string &kind, uint64_t t0_ns, uint64_t t1_ns)
void TraceSetBitColor(const std::string &bitLabel, const std::string &color)