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
68 TraceWriter::Instance().Open(Q2NS_EXAMPLE_TRACES_DIR "q2nsviz-entanglement-distribution-example.json");
69
70 Trace("Entanglement Distribution with 3 nodes");
71
72 Time::SetResolution(Time::NS);
73
74 NetController net;
75 net.SetQStateBackend(QStateBackend::Stab);
76
77 // --- Nodes ---
78 Ptr<QNode> node1 = net.CreateNode();
79 Ptr<QNode> node2 = net.CreateNode();
80 Ptr<QNode> node3 = net.CreateNode();
81
82 TraceCreateNode("Node1", 85, 50);
83 TraceCreateNode("Node2", 32, 80);
84 TraceCreateNode("Node3", 32, 19);
85
86 // --- Timing constants ---
87 // Values are illustrative.
88 const Time kSingleGate = NanoSeconds(100); // single-qubit gate
89 const Time kTwoQGate = NanoSeconds(300); // two-qubit gate
90 const Time kQDelay = NanoSeconds(100); // quantum link propagation (~20 m fiber)
91 const Time kClassical = kQDelay; // classical propagation = quantum (~20 m fiber)
92
93 // --- Quantum links (full mesh) ---
94 auto ch12 = net.InstallQuantumLink(node1, node2);
95 auto ch13 = net.InstallQuantumLink(node1, node3);
96 auto ch23 = net.InstallQuantumLink(node2, node3);
97 ch12->SetAttribute("Delay", TimeValue(kQDelay));
98 ch13->SetAttribute("Delay", TimeValue(kQDelay));
99 ch23->SetAttribute("Delay", TimeValue(kQDelay));
100
101 TraceCreateChannel("Node1", "Node2", "quantum");
102 TraceCreateChannel("Node1", "Node2", "classical");
103 TraceCreateChannel("Node1", "Node3", "quantum");
104 TraceCreateChannel("Node1", "Node3", "classical");
105 TraceCreateChannel("Node2", "Node3", "quantum");
106 TraceCreateChannel("Node2", "Node3", "classical");
107 // Reverse classical channels for ACK traffic
108 TraceCreateChannel("Node2", "Node1", "classical");
109 TraceCreateChannel("Node3", "Node1", "classical");
110 TraceCreateChannel("Node3", "Node2", "classical");
111
112 // --- Classical network (full mesh p2p) ---
113 InternetStackHelper internet;
114 internet.Install(node1);
115 internet.Install(node2);
116 internet.Install(node3);
117
118 PointToPointHelper p2p;
119 p2p.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
120 p2p.SetChannelAttribute("Delay", StringValue("100ns"));
121
122 Ipv4AddressHelper ipv4;
123
124 ipv4.SetBase("10.1.1.0", "255.255.255.0");
125 auto ifs12 = ipv4.Assign(p2p.Install(node1, node2));
126
127 ipv4.SetBase("10.1.2.0", "255.255.255.0");
128 auto ifs13 = ipv4.Assign(p2p.Install(node1, node3));
129
130 ipv4.SetBase("10.1.3.0", "255.255.255.0");
131 auto ifs23 = ipv4.Assign(p2p.Install(node2, node3));
132
133 Ipv4GlobalRoutingHelper::PopulateRoutingTables();
134
135 // ---------------------------------------------------------------------------
136 // UDP sockets for classical ACKs
137 // Node2 -> Node1 (Bell pair 0 confirmed)
138 // Node3 -> Node1 (Bell pair 1 confirmed)
139 // Node3 -> Node2 (Bell pair 2 confirmed)
140 // ---------------------------------------------------------------------------
141
142 // Node1 ACK-receive socket (receives ACKs from Node2 and Node3)
143 auto node1AckRx = Socket::CreateSocket(node1, UdpSocketFactory::GetTypeId());
144 node1AckRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kAckPort));
145 int node1AcksReceived = 0;
146 node1AckRx->SetRecvCallback([&](Ptr<Socket> sock) {
147 Address from;
148 while (sock->RecvFrom(from)) {
149 ++node1AcksReceived;
150 TraceNodeText("Node1", StrCat("ACK received (", node1AcksReceived, "/2 links confirmed)"));
151 std::cout << "[NODE1] ACK received (" << node1AcksReceived << "/2)\n";
152 if (node1AcksReceived == 2)
153 Trace("Node1: both Bell pair links confirmed");
154 }
155 });
156
157 // Node2 ACK-receive socket (receives ACK from Node3 for Bell pair 2)
158 auto node2AckRx = Socket::CreateSocket(node2, UdpSocketFactory::GetTypeId());
159 node2AckRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kAckPort));
160 node2AckRx->SetRecvCallback([&](Ptr<Socket> sock) {
161 Address from;
162 while (sock->RecvFrom(from)) {
163 TraceNodeText("Node2", "ACK from Node3: Bell pair 2 link confirmed");
164 Trace("Node2: Bell pair 2 confirmed by Node3");
165 std::cout << "[NODE2] ACK from Node3 received\n";
166 }
167 });
168
169 // Pre-created transmit sockets: addresses from interface containers.
170 // ifs12.GetAddress(0) = Node1 on the 1-2 subnet, etc.
171 auto node2TxSock = Socket::CreateSocket(node2, UdpSocketFactory::GetTypeId());
172 node2TxSock->Connect(InetSocketAddress(ifs12.GetAddress(0), kAckPort));
173
174 auto node3ToNode1Sock = Socket::CreateSocket(node3, UdpSocketFactory::GetTypeId());
175 node3ToNode1Sock->Connect(InetSocketAddress(ifs13.GetAddress(0), kAckPort));
176
177 auto node3ToNode2Sock = Socket::CreateSocket(node3, UdpSocketFactory::GetTypeId());
178 node3ToNode2Sock->Connect(InetSocketAddress(ifs23.GetAddress(0), kAckPort));
179
180 // Node2 qubit-receive callback (Bell pair 0: q0b arrives from Node1)
181 node2->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
182 TraceNodeText("Node2", StrCat("Qubit ", q->GetLabel(), " arrived - entangled with Node1"));
183 std::cout << "[NODE2] Qubit " << q->GetLabel() << " arrived\n";
184 node2TxSock->Send(Create<Packet>(1));
185 TraceSendPacket("Node2", "Node1", Simulator::Now(), Simulator::Now() + kClassical,
186 StrCat("ACK: ", q->GetLabel(), " received"), "udp");
187 });
188
189 // Node3 qubit-receive callback (Bell pairs 1 and 2 arrive)
190 // bell_1_b comes from Node1; bell_2_b comes from Node2.
191 int node3QubitCount = 0;
192 node3->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
193 ++node3QubitCount;
194 TraceNodeText("Node3", StrCat("Qubit ", q->GetLabel(), " arrived (", node3QubitCount, "/2)"));
195 std::cout << "[NODE3] Qubit " << q->GetLabel() << " arrived\n";
196 if (node3QubitCount == 1) {
197 node3ToNode1Sock->Send(Create<Packet>(1));
198 TraceSendPacket("Node3", "Node1", Simulator::Now(), Simulator::Now() + kClassical,
199 StrCat("ACK: ", q->GetLabel(), " received"), "udp");
200 } else {
201 node3ToNode2Sock->Send(Create<Packet>(1));
202 TraceSendPacket("Node3", "Node2", Simulator::Now(), Simulator::Now() + kClassical,
203 StrCat("ACK: ", q->GetLabel(), " received"), "udp");
204 }
205 });
206
207 // --- Bell pair 0: Node1 <-> Node2, base T = 1 us ---
208 Simulator::Schedule(MicroSeconds(1), [&] {
209 Trace("Step 1: Node1 initializes Bell pair 0 (for Node2)");
210 TraceNodeText("Node1", "Step 1: init Bell pair 0");
211
212 auto q0a = node1->CreateQubit();
213 q0a->SetLabel("bell_0_a");
214 auto q0b = node1->CreateQubit();
215 q0b->SetLabel("bell_0_b");
216
217 TraceCreateQubit("Node1", "bell_0_a");
218 TraceCreateQubit("Node1", "bell_0_b");
219
220 // T + 100 ns: H(q0a)
221 Simulator::Schedule(kSingleGate, [=]() {
222 node1->Apply(gates::H(), {q0a});
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 TraceSendQubit("bell_0_b", "Node1", "Node2", t0, t0 + kQDelay);
236 TraceNodeText("Node1", "bell_0_b in transit to Node2");
237 std::cout << "[STEP 1] Bell pair 0 sent: Node1 -> Node2\n";
238 });
239 });
240 });
241 });
242
243 // --- Bell pair 1: Node1 <-> Node3 ---
244 // (Bell pair 0 finishes at ~1.65 us; 3 us gives clear temporal separation.)
245 Simulator::Schedule(MicroSeconds(3), [&] {
246 Trace("Step 2: Node1 initializes Bell pair 1 (for Node3)");
247 TraceNodeText("Node1", "Step 2: init Bell pair 1");
248
249 auto q1a = node1->CreateQubit();
250 q1a->SetLabel("bell_1_a");
251 auto q1b = node1->CreateQubit();
252 q1b->SetLabel("bell_1_b");
253
254 TraceCreateQubit("Node1", "bell_1_a");
255 TraceCreateQubit("Node1", "bell_1_b");
256
257 Simulator::Schedule(kSingleGate, [=]() {
258 node1->Apply(gates::H(), {q1a});
259 TraceNodeText("Node1", "H(bell_1_a)");
260
261 Simulator::Schedule(kTwoQGate, [=]() {
262 node1->Apply(gates::CNOT(), {q1a, q1b});
263 TraceEntangle({"bell_1_a", "bell_1_b"}, kTwoQGate);
264 TraceNodeText("Node1", "Bell pair 1 ready");
265
266 Simulator::Schedule(kSingleGate, [=]() {
267 auto t0 = Simulator::Now();
268 node1->Send(q1b, node3->GetId());
269 TraceSendQubit("bell_1_b", "Node1", "Node3", t0, t0 + kQDelay);
270 TraceNodeText("Node1", "bell_1_b in transit to Node3");
271 std::cout << "[STEP 2] Bell pair 1 sent: Node1 -> Node3\n";
272 });
273 });
274 });
275 });
276
277 // --- Bell pair 2: Node2 <-> Node3 ---
278 Simulator::Schedule(MicroSeconds(5), [&] {
279 Trace("Step 3: Node2 initializes Bell pair 2 (for Node3)");
280 TraceNodeText("Node2", "Step 3: init Bell pair 2");
281
282 auto q2a = node2->CreateQubit();
283 q2a->SetLabel("bell_2_a");
284 auto q2b = node2->CreateQubit();
285 q2b->SetLabel("bell_2_b");
286
287 TraceCreateQubit("Node2", "bell_2_a");
288 TraceCreateQubit("Node2", "bell_2_b");
289
290 Simulator::Schedule(kSingleGate, [=]() {
291 node2->Apply(gates::H(), {q2a});
292 TraceNodeText("Node2", "H(bell_2_a)");
293
294 Simulator::Schedule(kTwoQGate, [=]() {
295 node2->Apply(gates::CNOT(), {q2a, q2b});
296 TraceEntangle({"bell_2_a", "bell_2_b"}, kTwoQGate);
297 TraceNodeText("Node2", "Bell pair 2 ready");
298
299 Simulator::Schedule(kSingleGate, [=]() {
300 auto t0 = Simulator::Now();
301 node2->Send(q2b, node3->GetId());
302 TraceSendQubit("bell_2_b", "Node2", "Node3", t0, t0 + kQDelay);
303 TraceNodeText("Node2", "bell_2_b in transit to Node3");
304 std::cout << "[STEP 3] Bell pair 2 sent: Node2 -> Node3\n";
305 });
306 });
307 });
308 });
309
310 // --- Final trace at 20 us (all ACKs have arrived by ~16 us) ---
311 Simulator::Schedule(MicroSeconds(20), [&] {
312 Trace("All pairwise Bell pairs established - 3 independent bipartite entangled links");
313 std::cout << "[DONE] All Bell pairs distributed\n";
314 });
315
316 Simulator::Stop(MilliSeconds(1));
317 Simulator::Run();
318 Simulator::Destroy();
319
321 std::cout << "[DONE] Entanglement distribution finished\n";
322 return 0;
323}
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 TraceSendQubit(const std::string &bitLabel, const std::string &from, const std::string &to, uint64_t t0_ns, uint64_t t1_ns)
void TraceCreateQubit(const std::string &node, const std::string &bitLabel)
void TraceNodeText(const std::string &node, const std::string &text)
void TraceEntangle(const std::vector< std::string > &bits)
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 TraceSendPacket(const std::string &from, const std::string &to, uint64_t t0_ns, uint64_t t1_ns, const std::string &label, const std::string &protocol="")