Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2nsviz-channel-loss-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-channel-loss-example.cc
11 * @brief Entanglement distribution with photon loss, receiver-side NACK, and retry.
12 *
13 * Source generates a Bell pair and sends the flying qubit over a lossy channel.
14 * A classical notification packet is sent concurrently so Destination can arm a
15 * receive-timeout. On qubit arrival Destination sends an ACK, on timeout it sends
16 * a NACK.
17 *
18 * Tunable parameters:
19 * --run RNG run index (default 1).
20 * --loss-prob Per-qubit loss probability [0,1] (default 0.8).
21 *
22 * Timing model (illustrative):
23 * kSingleGate = 100 ns (single-qubit gate)
24 * kTwoQGate = 300 ns (two-qubit gate)
25 * kQDelay = 10 ns (quantum channel propagation, ~2 m fiber)
26 * kDstTimeout = 5 ns (scheduled from notification arrival; qubit arrives ~2.5 ns
27 * before the notify packet at 100 Mbps, so the timeout fires
28 * safely after a successful reception and before any retry)
29 *
30 * Visualization output is written to
31 * examples/example_traces/q2nsviz-channel-loss-example.json and can be loaded in the
32 * q2nsviz viewer (src/q2ns/utils/q2nsviz-serve.sh).
33 */
34
35
36#include "ns3/core-module.h"
37#include "ns3/internet-module.h"
38#include "ns3/network-module.h"
39#include "ns3/point-to-point-module.h"
40#include "ns3/simulator.h"
41
42#include "ns3/q2ns-netcontroller.h"
43#include "ns3/q2ns-qgate.h"
44#include "ns3/q2ns-qmap.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 <functional>
52#include <iostream>
53
54using namespace ns3;
55using namespace q2ns;
56
57static const uint16_t kCtrlPort = 9200; // Destination -> Source (ACK/NACK)
58static const uint16_t kNotifyPort = 9201; // Source -> Destination (qubit notification)
59static const int kMaxAttempts = 20;
60
61int main(int argc, char** argv) {
62 std::cout << "[DEMO] Channel-loss entanglement distribution starting\n";
63
64 uint32_t run = 1;
65 double lossProb = 0.8;
66
67 RngSeedManager::SetSeed(1);
68
69 CommandLine cmd;
70 cmd.AddValue("run", "RNG run index", run);
71 cmd.AddValue("loss-prob", "Per-qubit loss probability [0,1]", lossProb);
72 cmd.Parse(argc, argv);
73
74 RngSeedManager::SetRun(run);
75
76 TraceWriter::Instance().Open(Q2NS_EXAMPLE_TRACES_DIR "q2nsviz-channel-loss-example.json");
77 Trace("Entanglement Distribution with Photon Loss (loss-prob=", lossProb, " run=", run, ")");
78
79 Time::SetResolution(Time::NS);
80
81 NetController net;
82 net.SetQStateBackend(QStateBackend::Ket);
83
84 // --- Nodes ---
85 auto src = net.CreateNode();
86 auto dst = net.CreateNode();
87
88 TraceCreateNode("Source", 25, 50);
89 TraceCreateNode("Destination", 75, 50);
90
91 // --- Timing constants ---
92 const Time kSingleGate = NanoSeconds(100);
93 const Time kTwoQGate = NanoSeconds(300);
94 const Time kQDelay = NanoSeconds(10);
95 const Time kClassical = kQDelay;
96 // kDstTimeout is scheduled from notification arrival (not from qubit send time).
97 // Timeout fires at t_notify + 5 ns (before a new attempt is made)
98 const Time kDstTimeout = NanoSeconds(5);
99
100 // --- Quantum link (lossy) ---
101 auto ch = net.InstallQuantumLink(src, dst);
102 ch->SetAttribute("Delay", TimeValue(kQDelay));
103
104 auto lossMap = CreateObject<LossQMap>();
105 lossMap->SetAttribute("Probability", DoubleValue(lossProb));
106 ch->SetQMap(lossMap);
107
108 TraceCreateChannel("Source", "Destination", "quantum");
109
110 // --- Classical network ---
111 InternetStackHelper internet;
112 internet.Install(src);
113 internet.Install(dst);
114
115 PointToPointHelper p2p;
116 p2p.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
117 p2p.SetChannelAttribute("Delay", StringValue("10ns"));
118
119 Ipv4AddressHelper ipv4;
120 ipv4.SetBase("10.1.1.0", "255.255.255.0");
121 auto ifsSD = ipv4.Assign(p2p.Install(src, dst));
122 Ipv4GlobalRoutingHelper::PopulateRoutingTables();
123 net.AssignStreams(0);
124
125 const Ipv4Address kSrcAddr = ifsSD.GetAddress(0);
126 const Ipv4Address kDstAddr = ifsSD.GetAddress(1);
127
128 TraceCreateChannel("Destination", "Source", "classical");
129 TraceCreateChannel("Source", "Destination", "classical");
130
131 // --- Sockets ---
132 // ACK/NACK: Destination <-> Source (byte[0]: 0x01=ACK, 0x00=NACK; byte[1]: attempt)
133 auto srcCtrlRx = Socket::CreateSocket(src, UdpSocketFactory::GetTypeId());
134 srcCtrlRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kCtrlPort));
135 auto dstCtrlTx = Socket::CreateSocket(dst, UdpSocketFactory::GetTypeId());
136 dstCtrlTx->Connect(InetSocketAddress(kSrcAddr, kCtrlPort));
137
138 // Notify: Source <-> Destination
139 auto dstNotifyRx = Socket::CreateSocket(dst, UdpSocketFactory::GetTypeId());
140 dstNotifyRx->Bind(InetSocketAddress(Ipv4Address::GetAny(), kNotifyPort));
141 auto srcNotifyTx = Socket::CreateSocket(src, UdpSocketFactory::GetTypeId());
142 srcNotifyTx->Connect(InetSocketAddress(kDstAddr, kNotifyPort));
143
144 // --- Attempt state ---
145 int attemptNum = 0;
146 int dstLatestArrived = 0;
147 std::shared_ptr<Qubit> currentMemQ;
148 std::string currentMemLabel;
149
150 std::function<void()> scheduleAttempt;
151
152 // Dest Callback: notification received - arm receive-timeout | NACK on expiry
153 dstNotifyRx->SetRecvCallback([&](Ptr<Socket> sock) {
154 Address from;
155 Ptr<Packet> pkt;
156 while ((pkt = sock->RecvFrom(from))) {
157 uint8_t attByte = 0;
158 pkt->CopyData(&attByte, 1);
159 const int att = static_cast<int>(attByte);
160
161 std::cout << "[DEST] Notify for attempt " << att << " - arming timeout\n";
162 TraceNodeText("Destination", StrCat("Expecting qubit (attempt ", att, ")"));
163
164 Simulator::Schedule(kDstTimeout, [&, att]() {
165 if (dstLatestArrived >= att) return;
166 std::cout << "[DEST] Attempt " << att << " timeout - qubit lost, sending timeout signal\n";
167 TraceNodeText("Destination", StrCat("Timeout: attempt ", att, " - qubit lost"));
168 TraceRemoveQubit("fly_" + std::to_string(att), "lost");
169 const auto tNow = Simulator::Now();
170 uint8_t buf[2] = {0x00, static_cast<uint8_t>(att)};
171 dstCtrlTx->Send(Create<Packet>(buf, 2));
172 TraceSendPacket("Destination", "Source", tNow, tNow + kClassical,
173 StrCat("timeout: attempt ", att), "udp");
174 });
175 }
176 });
177
178 // Dest Callback: qubit arrived - record receipt, send ACK
179 dst->SetRecvCallback([&](std::shared_ptr<Qubit> q) {
180 dstLatestArrived = attemptNum;
181 TraceNodeText("Destination", StrCat(q->GetLabel(), " arrived"));
182 Trace("Destination received ", q->GetLabel());
183 std::cout << "[DEST] " << q->GetLabel() << " arrived\n";
184 const auto tNow = Simulator::Now();
185 uint8_t buf[2] = {0x01, static_cast<uint8_t>(attemptNum)};
186 dstCtrlTx->Send(Create<Packet>(buf, 2));
187 TraceSendPacket("Destination", "Source", tNow, tNow + kClassical,
188 StrCat("ACK: ", q->GetLabel()), "udp");
189 });
190
191 // Source Callback: ACK (0x01) or NACK (0x00) from Destination
192 srcCtrlRx->SetRecvCallback([&](Ptr<Socket> sock) {
193 Address from;
194 Ptr<Packet> pkt;
195 while ((pkt = sock->RecvFrom(from))) {
196 uint8_t buf[2] = {0, 0};
197 pkt->CopyData(buf, 2);
198 const bool isAck = (buf[0] == 0x01);
199 const int att = static_cast<int>(buf[1]);
200 if (isAck) {
201 Trace("Source received ACK - entanglement established on attempt ", attemptNum);
202 TraceNodeText("Source", "ACK received - entanglement distributed!");
203 std::cout << "[SOURCE] ACK on attempt " << attemptNum << " - success\n";
204 Simulator::Stop();
205 } else {
206 std::cout << "[SOURCE] Timeout for attempt " << att << " - retrying\n";
207 Trace("Timeout for attempt ", att, " - qubit lost");
208 TraceNodeText("Source", StrCat("Timeout: attempt ", att, " - discarding mem qubit"));
209 TraceRemoveQubit(currentMemLabel, "discarded");
210 currentMemQ.reset();
211 Simulator::Schedule(NanoSeconds(100), scheduleAttempt);
212 }
213 }
214 });
215
216 // scheduleAttempt: generate Bell pair, dispatch flying qubit + notify packet.
217 scheduleAttempt = [&]() {
218 ++attemptNum;
219
220 if (attemptNum > kMaxAttempts) {
221 std::cout << "[SOURCE] Max attempts (" << kMaxAttempts << ") reached - giving up\n";
222 TraceNodeText("Source", StrCat("Max attempts (", kMaxAttempts, ") reached - aborting"));
223 Simulator::Stop();
224 return;
225 }
226
227 const std::string memLabel = "mem_" + std::to_string(attemptNum);
228 const std::string flyLabel = "fly_" + std::to_string(attemptNum);
229
230 Trace("Attempt ", attemptNum, ": Source generates Bell pair (", memLabel, ", ", flyLabel, ")");
231 TraceNodeText("Source", StrCat("Attempt ", attemptNum, ": generating Bell pair"));
232
233 auto memQ = src->CreateQubit(memLabel);
234 auto flyQ = src->CreateQubit(flyLabel);
235 currentMemQ = memQ;
236 currentMemLabel = memLabel;
237
238 TraceCreateQubit("Source", memLabel);
239 TraceCreateQubit("Source", flyLabel);
240
241 const int thisAttempt = attemptNum;
242 const uint8_t attByte = static_cast<uint8_t>(thisAttempt);
243
244 // Step 1 (kSingleGate): H on memQ creates superposition |+>.
245 Simulator::Schedule(kSingleGate, [=, &srcNotifyTx]() {
246 src->Apply(gates::H(), {memQ});
247 TraceNodeText("Source", StrCat("H(", memLabel, ")"));
248
249 // Step 2 (kTwoQGate): CNOT(memQ, flyQ) entangles the pair into |Phi+>.
250 Simulator::Schedule(kTwoQGate, [=, &srcNotifyTx]() {
251 src->Apply(gates::CNOT(), {memQ, flyQ});
252 TraceEntangle({memLabel, flyLabel}, kTwoQGate);
253 TraceNodeText("Source", StrCat("Bell pair ready: ", memLabel, " + ", flyLabel));
254
255 // Step 3 (kSingleGate): Qubit travels the quantum channel (kQDelay = 10 ns)
256 // Notify packet travels P2P (10 ns propagation + ~2.5 ns tx at 100 Mbps).
257 // Please note: the qubit always arrives ~2.5 ns before the notification
258 // when not lost. kDstTimeout is scheduled from notification arrival.
259 Simulator::Schedule(kSingleGate, [=, &srcNotifyTx]() {
260 const auto tSend = Simulator::Now();
261
262 src->Send(flyQ, dst->GetId());
263 TraceSendQubit(flyLabel, "Source", "Destination", tSend, tSend + kQDelay);
264
265 // Notification arms the receive-timeout at Destination.
266 srcNotifyTx->Send(Create<Packet>(&attByte, 1));
267 TraceSendPacket("Source", "Destination", tSend, tSend + kClassical,
268 StrCat("notify: attempt ", thisAttempt), "udp");
269
270 TraceNodeText("Source", StrCat(flyLabel, " sent (attempt ", thisAttempt, ")"));
271 std::cout << "[SOURCE] Attempt " << thisAttempt << ": " << flyLabel << " sent\n";
272 });
273 });
274 });
275 };
276
277 // Start attempt 1 at t=1 us
278 Simulator::Schedule(MicroSeconds(1), scheduleAttempt);
279
280 Simulator::Stop(MilliSeconds(1));
281 Simulator::Run();
282 Simulator::Destroy();
283
285 std::cout << "[DONE] Channel-loss example finished\n";
286 return 0;
287}
static TraceWriter & Instance()
void Open(const std::string &path)
Main user-facing facade for creating and configuring a quantum network.
int64_t AssignStreams(int64_t stream)
Assign RNG streams to q2ns-owned random sources.
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 kCtrlPort
static const uint16_t kNotifyPort
static const int kMaxAttempts
void TraceRemoveQubit(const std::string &bitLabel, const std::string &reason="discarded", uint64_t t_ns=NowNs())
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="")