Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2nsviz-repeater-swap-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-repeater-swap-example.cc
11 * @brief Entanglement swapping via a quantum repeater (source-centric BSG model).
12 *
13 * Two dedicated Bell-State Generator (BSG) source nodes create the link-level
14 * entanglement and distribute the qubits:
15 * Src1 generates (fly_a, rep_a): fly_a -> Alice, rep_a -> Repeater.
16 * Src2 generates (rep_b, fly_b): rep_b -> Repeater, fly_b -> Bob.
17 *
18 * Once Alice and Bob receive their flying qubits they each send a classical ACK
19 * to the Repeater. When both ACKs arrive the Repeater performs a Bell-State
20 * Measurement (BSM) on mem_a and mem_b, swapping entanglement end-to-end.
21 * BSM outcomes are forwarded to Bob, who applies the Pauli corrections.
22 *
23 * Timing model (illustrative):
24 * kSingleGate = 100 ns (single-qubit gate)
25 * kTwoQGate = 300 ns (two-qubit gate / BSM)
26 * kQDelay = 10 ns (quantum channel propagation, ~2 m fiber)
27 *
28 * Visualization output is written to
29 * examples/example_traces/q2nsviz-repeater-swap-example.json and can be loaded in the
30 * q2nsviz viewer (src/q2ns/utils/q2nsviz-serve.sh).
31 */
32
33#include "ns3/core-module.h"
34#include "ns3/internet-module.h"
35#include "ns3/network-module.h"
36#include "ns3/point-to-point-module.h"
37#include "ns3/simulator.h"
38
39#include "ns3/q2ns-netcontroller.h"
40#include "ns3/q2ns-qgate.h"
41#include "ns3/q2ns-qnode.h"
42#include "ns3/q2ns-qubit.h"
43
44#include "ns3/q2nsviz-trace-writer.h"
45#include "ns3/q2nsviz-trace.h"
46
47#include <iostream>
48
49using namespace ns3;
50using namespace q2ns;
51
52static const uint16_t kAckPort = 9200; // Repeater listens for arrival ACKs
53static const uint16_t kCtrlPort = 9100; // Bob listens for BSM corrections
54
55int main(int argc, char** argv) {
56 std::cout << "[DEMO] Repeater-centric entanglement swap starting\n";
57
58 RngSeedManager::SetSeed(1);
59 RngSeedManager::SetRun(1);
60
61 CommandLine cmd;
62 cmd.Parse(argc, argv);
63
64 TraceWriter::Instance().Open(Q2NS_EXAMPLE_TRACES_DIR "q2nsviz-repeater-swap-example.json");
65 Trace("Repeater-Centric Entanglement Swap");
66
67 Time::SetResolution(Time::NS);
68
69 NetController net;
70
71 // --- Nodes ---
72 auto alice = net.CreateNode();
73 auto src1 = net.CreateNode();
74 auto repeater = net.CreateNode();
75 auto src2 = net.CreateNode();
76 auto bob = net.CreateNode();
77
78 TraceCreateNode("Alice", 10, 50);
79 TraceCreateNode("Src1", 30, 30);
80 TraceCreateNode("Repeater", 50, 50);
81 TraceCreateNode("Src2", 70, 30);
82 TraceCreateNode("Bob", 90, 50);
83
84 // --- Timing constants ---
85 const Time kSingleGate = NanoSeconds(100);
86 const Time kTwoQGate = NanoSeconds(300);
87 const Time kQDelay = NanoSeconds(10);
88 const Time kClassical = kQDelay;
89
90 // --- Quantum links (Src1 distributes to Alice and Repeater; Src2 to Repeater and Bob) ---
91 auto chS1A = net.InstallQuantumLink(src1, alice);
92 auto chS1R = net.InstallQuantumLink(src1, repeater);
93 auto chS2R = net.InstallQuantumLink(src2, repeater);
94 auto chS2B = net.InstallQuantumLink(src2, bob);
95 chS1A->SetAttribute("Delay", TimeValue(kQDelay));
96 chS1R->SetAttribute("Delay", TimeValue(kQDelay));
97 chS2R->SetAttribute("Delay", TimeValue(kQDelay));
98 chS2B->SetAttribute("Delay", TimeValue(kQDelay));
99
100 TraceCreateChannel("Src1", "Alice", "quantum");
101 TraceCreateChannel("Src1", "Repeater", "quantum");
102 TraceCreateChannel("Src2", "Repeater", "quantum");
103 TraceCreateChannel("Src2", "Bob", "quantum");
104
105 // --- Classical network (Alice, Repeater, Bob only; Src1 and Src2 are quantum-only) ---
106 InternetStackHelper internet;
107 internet.Install(alice);
108 internet.Install(repeater);
109 internet.Install(bob);
110
111 PointToPointHelper p2p;
112 p2p.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
113 p2p.SetChannelAttribute("Delay", StringValue("10ns"));
114
115 Ipv4AddressHelper ipv4;
116 ipv4.SetBase("10.1.1.0", "255.255.255.0");
117 auto ifsAR = ipv4.Assign(p2p.Install(alice, repeater));
118 ipv4.SetBase("10.1.2.0", "255.255.255.0");
119 auto ifsRB = ipv4.Assign(p2p.Install(repeater, bob));
120 Ipv4GlobalRoutingHelper::PopulateRoutingTables();
121
122 const Ipv4Address kRepeaterAddr = ifsAR.GetAddress(1);
123 const Ipv4Address kBobAddr = ifsRB.GetAddress(1);
124
125 TraceCreateChannel("Alice", "Repeater", "classical");
126 TraceCreateChannel("Bob", "Repeater", "classical");
127 TraceCreateChannel("Repeater", "Bob", "classical");
128
129 // --- Sockets ---
130 auto repeaterAckRx = Socket::CreateSocket(repeater, UdpSocketFactory::GetTypeId());
131 repeaterAckRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kAckPort));
132
133 auto aliceTx = Socket::CreateSocket(alice, UdpSocketFactory::GetTypeId());
134 aliceTx->Connect(InetSocketAddress(kRepeaterAddr, kAckPort));
135
136 auto bobAckTx = Socket::CreateSocket(bob, UdpSocketFactory::GetTypeId());
137 bobAckTx->Connect(InetSocketAddress(kRepeaterAddr, kAckPort));
138
139 auto repeaterTx = Socket::CreateSocket(repeater, UdpSocketFactory::GetTypeId());
140 repeaterTx->Connect(InetSocketAddress(kBobAddr, kCtrlPort));
141
142 auto bobCtrlRx = Socket::CreateSocket(bob, UdpSocketFactory::GetTypeId());
143 bobCtrlRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kCtrlPort));
144
145 // --- Shared state ---
146 int acksReceived = 0;
147 bool bsmDone = false;
148
149 // Repeater memory qubits (filled by quantum recv callback)
150 std::shared_ptr<Qubit> repMem1;
151 std::shared_ptr<Qubit> repMem2;
152 int repQubitsRxd = 0;
153
154 // Repeater: receive mem_a from Src1 and mem_b from Src2
155 repeater->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
156 ++repQubitsRxd;
157 if (repQubitsRxd == 1) {
158 repMem1 = q;
159 TraceNodeText("Repeater", StrCat(q->GetLabel(), " arrived (1/2)"));
160 std::cout << "[REPEATER][quantum] " << q->GetLabel() << " arrived (1/2)\n";
161 } else {
162 repMem2 = q;
163 TraceNodeText("Repeater", StrCat(q->GetLabel(), " arrived (2/2) - ready for BSM"));
164 std::cout << "[REPEATER][quantum] " << q->GetLabel() << " arrived (2/2)\n";
165 }
166 });
167
168 struct BobCtx {
169 std::shared_ptr<Qubit> qFlyB;
170 bool qubitArrived = false;
171 bool bitsArrived = false;
172 int m1 = 0;
173 int m2 = 0;
174 };
175 BobCtx bobCtx;
176
177 auto tryBobCorrections = [&]() {
178 if (!bobCtx.qubitArrived || !bobCtx.bitsArrived)
179 return;
180 Trace("Bob applies Pauli corrections X^m2=", bobCtx.m2, " Z^m1=", bobCtx.m1);
181 if (bobCtx.m2) {
182 bob->Apply(gates::X(), {bobCtx.qFlyB});
183 TraceNodeText("Bob", "X correction applied (m2=1)");
184 } else {
185 TraceNodeText("Bob", "X correction skipped (m2=0)");
186 }
187 if (bobCtx.m1) {
188 bob->Apply(gates::Z(), {bobCtx.qFlyB});
189 TraceNodeText("Bob", "Z correction applied (m1=1)");
190 } else {
191 TraceNodeText("Bob", "Z correction skipped (m1=0)");
192 }
193 TraceRemoveCbit("m1");
194 TraceRemoveCbit("m2");
195 Trace("End-to-end entanglement established between Alice and Bob");
196 std::cout << "[DONE] End-to-end entanglement established\n";
197 Simulator::Stop();
198 };
199
200 // Alice: flying qubit arrived -> send ACK to Repeater
201 alice->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
202 TraceNodeText("Alice", StrCat(q->GetLabel(), " arrived"));
203 std::cout << "[ALICE][quantum] " << q->GetLabel() << " arrived at t="
204 << Simulator::Now().GetNanoSeconds() << " ns\n";
205 aliceTx->Send(Create<Packet>(1));
206 const auto tNow = Simulator::Now();
207 TraceSendPacket("Alice", "Repeater", tNow, tNow + kClassical, "ACK: fly_a received", "udp");
208 });
209
210 // Bob: flying qubit arrived -> send ACK to Repeater; store qubit for corrections
211 bob->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
212 bobCtx.qFlyB = q;
213 bobCtx.qubitArrived = true;
214 TraceNodeText("Bob", StrCat(q->GetLabel(), " arrived - awaiting BSM corrections"));
215 std::cout << "[BOB][quantum] " << q->GetLabel() << " arrived at t="
216 << Simulator::Now().GetNanoSeconds() << " ns\n";
217 bobAckTx->Send(Create<Packet>(1));
218 const auto tNow = Simulator::Now();
219 TraceSendPacket("Bob", "Repeater", tNow, tNow + kClassical, "ACK: fly_b received", "udp");
220 tryBobCorrections();
221 });
222
223 // Bob: classical corrections from Repeater
224 bobCtrlRx->SetRecvCallback([&](Ptr<Socket> sock) {
225 Address from;
226 while (Ptr<Packet> p = sock->RecvFrom(from)) {
227 uint8_t bits[2] = {0, 0};
228 p->CopyData(bits, 2);
229 bobCtx.m1 = bits[0] & 1;
230 bobCtx.m2 = bits[1] & 1;
231 bobCtx.bitsArrived = true;
232 Trace("Bob receives corrections m1=", bobCtx.m1, " m2=", bobCtx.m2);
233 TraceNodeText("Bob", StrCat("Corrections received: m1=", bobCtx.m1, " m2=", bobCtx.m2));
234 std::cout << "[BOB][CTRL] corrections m1=" << bobCtx.m1 << " m2=" << bobCtx.m2 << "\n";
235 tryBobCorrections();
236 }
237 });
238
239 // Repeater: both ACKs received -> perform BSM and forward outcomes to Bob
240 repeaterAckRx->SetRecvCallback([&](Ptr<Socket> sock) {
241 Address from;
242 while (sock->RecvFrom(from)) {
243 ++acksReceived;
244 TraceNodeText("Repeater", StrCat("ACK received (", acksReceived, "/2)"));
245 std::cout << "[REPEATER] ACK " << acksReceived << "/2 received\n";
246
247 if (acksReceived == 2 && !bsmDone) {
248 bsmDone = true;
249
250 if (!repMem1 || !repMem2) {
251 std::cerr << "[REPEATER] ERROR: memory qubits not ready for BSM!\n";
252 return;
253 }
254
255 Trace("Repeater performs BSM on rep_a and rep_b");
256 TraceNodeText("Repeater", "BSM: measuring rep_a and rep_b");
257 auto [m1, m2] = repeater->MeasureBell(repMem1, repMem2);
258 std::cout << "[REPEATER][BSM] m1=" << m1 << " m2=" << m2 << "\n";
259
260 TraceMeasure("rep_a", kTwoQGate, "Bell");
261 TraceMeasure("rep_b", kTwoQGate, "Bell");
262 TraceEntangle({"fly_a", "fly_b"});
263 TraceRemoveQubit("rep_a", "discarded");
264 TraceRemoveQubit("rep_b", "discarded");
265
266 const auto tNow = Simulator::Now();
267 uint8_t bits[2] = {(uint8_t)(m1 & 1), (uint8_t)(m2 & 1)};
268 repeaterTx->Send(Create<Packet>(bits, 2));
269
270 TraceCreateCbit("Repeater", "m1");
271 TraceCreateCbit("Repeater", "m2");
272 TraceSendCbit("m1", "Repeater", "Bob", tNow, tNow + kClassical);
273 TraceSendCbit("m2", "Repeater", "Bob", tNow, tNow + kClassical);
274 TraceSendPacket("Repeater", "Bob", tNow, tNow + kClassical,
275 StrCat("BSM corrections: m1=", m1, " m2=", m2), "udp");
276 TraceNodeText("Repeater", StrCat("Corrections m1=", m1, " m2=", m2, " sent to Bob"));
277 }
278 }
279 });
280
281 // Src1 and Src2 each generate a Bell pair and distribute qubits
282 Simulator::Schedule(MicroSeconds(1), [&]() {
283 Trace("Phase 1: Src1 and Src2 generate Bell pairs");
284 TraceNodeText("Src1", "Generating Bell pair (fly_a, rep_a)");
285 TraceNodeText("Src2", "Generating Bell pair (rep_b, fly_b)");
286
287 auto [flyA, memA] = src1->CreateBellPair();
288 flyA->SetLabel("fly_a");
289 memA->SetLabel("rep_a");
290 TraceCreateQubit("Src1", "fly_a");
291 TraceCreateQubit("Src1", "rep_a");
292
293 auto [memB, flyB] = src2->CreateBellPair();
294 memB->SetLabel("rep_b");
295 flyB->SetLabel("fly_b");
296 TraceCreateQubit("Src2", "rep_b");
297 TraceCreateQubit("Src2", "fly_b");
298
299 Simulator::Schedule(kTwoQGate, [&, flyA, memA, memB, flyB]() {
300 TraceEntangle({"fly_a", "rep_a"}, kTwoQGate);
301 TraceEntangle({"rep_b", "fly_b"}, kTwoQGate);
302 TraceNodeText("Src1", "|Phi+>_S1 ready (fly_a, rep_a)");
303 TraceNodeText("Src2", "|Phi+>_S2 ready (rep_b, fly_b)");
304 std::cout << "[SRC1] Bell pair ready\n";
305 std::cout << "[SRC2] Bell pair ready\n";
306
307 // Distribute: fly_a -> Alice, mem_a -> Repeater, mem_b -> Repeater, fly_b -> Bob
308 Simulator::Schedule(kSingleGate, [&, flyA, memA, memB, flyB]() {
309 Trace("Phase 2: distributing qubits to endpoints and repeater");
310 const auto tSend = Simulator::Now();
311
312 src1->Send(flyA, alice->GetId());
313 TraceSendQubit("fly_a", "Src1", "Alice", tSend, tSend + kQDelay);
314
315 src1->Send(memA, repeater->GetId());
316 TraceSendQubit("rep_a", "Src1", "Repeater", tSend, tSend + kQDelay);
317
318 src2->Send(memB, repeater->GetId());
319 TraceSendQubit("rep_b", "Src2", "Repeater", tSend, tSend + kQDelay);
320
321 src2->Send(flyB, bob->GetId());
322 TraceSendQubit("fly_b", "Src2", "Bob", tSend, tSend + kQDelay);
323
324 TraceNodeText("Src1", "fly_a -> Alice, rep_a -> Repeater");
325 TraceNodeText("Src2", "rep_b -> Repeater, fly_b -> Bob");
326 std::cout << "[SRC1] fly_a sent to Alice, rep_a sent to Repeater\n";
327 std::cout << "[SRC2] rep_b sent to Repeater, fly_b sent to Bob\n";
328 });
329 });
330 });
331
332 Simulator::Stop(MilliSeconds(1));
333 Simulator::Run();
334 Simulator::Destroy();
335
337 std::cout << "[DONE] Repeater swap finished\n";
338 return 0;
339}
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.
QGate X(ns3::Time d=ns3::Seconds(0))
Return the Pauli-X gate descriptor.
Definition q2ns-qgate.h:356
QGate Z(ns3::Time d=ns3::Seconds(0))
Return the Pauli-Z gate descriptor.
Definition q2ns-qgate.h:374
int main()
uint8_t m2
uint8_t m1
static const uint16_t kCtrlPort
static const uint16_t kAckPort
void TraceRemoveQubit(const std::string &bitLabel, const std::string &reason="discarded", uint64_t t_ns=NowNs())
std::string StrCat(Ts &&... parts)
void TraceSendCbit(const std::string &bitLabel, const std::string &from, const std::string &to, uint64_t t0_ns, uint64_t t1_ns)
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 TraceRemoveCbit(const std::string &bitLabel, uint64_t t_ns=NowNs())
void TraceMeasure(const std::string &bitLabel, const std::string &base="Z")
void TraceCreateCbit(const std::string &node, const std::string &bitLabel)
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="")