Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-teleportation-app.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 q2ns-teleportation-app.cc
11 * @brief Defines q2ns::TeleportationApp.
12 */
13#include "ns3/q2ns-teleportation-app.h"
14#include "ns3/q2ns-analysis.h"
15#include "ns3/q2ns-netcontroller.h"
16#include "ns3/q2ns-qgate.h"
17#include "ns3/q2ns-qnode.h"
18#include "ns3/q2ns-qstate-ket.h"
19#include "ns3/q2ns-qubit.h"
20
21
22#include "ns3/boolean.h"
23#include "ns3/bulk-send-helper.h"
24#include "ns3/data-rate.h"
25#include "ns3/double.h"
26#include "ns3/inet-socket-address.h"
27#include "ns3/internet-module.h"
28#include "ns3/ipv4.h"
29#include "ns3/log.h"
30#include "ns3/names.h"
31#include "ns3/node.h"
32#include "ns3/on-off-helper.h"
33#include "ns3/packet-sink-helper.h"
34#include "ns3/packet-sink.h"
35#include "ns3/packet.h"
36#include "ns3/pointer.h"
37#include "ns3/simulator.h"
38#include "ns3/string.h"
39#include "ns3/uinteger.h"
40
41
42#include <cmath>
43#include <limits>
44#include <sstream>
45#include <string>
46
47namespace q2ns {
48
49NS_LOG_COMPONENT_DEFINE("TeleportationApp");
50NS_OBJECT_ENSURE_REGISTERED(TeleportationApp);
51
53 static ns3::TypeId tid =
54 ns3::TypeId("q2ns::TeleportationApp")
55 .SetParent<ns3::Application>()
56 .SetGroupName("q2ns")
57
58 .AddAttribute("Role", "Session role for this app: 'source' (Alice) or 'sink' (Bob).",
59 ns3::StringValue("source"),
60 ns3::MakeStringAccessor(&TeleportationApp::m_role),
61 ns3::MakeStringChecker())
62
63 .AddAttribute("PeerQNode", "Quantum peer (destination) as a direct object pointer.",
64 ns3::PointerValue(), // default null
65 ns3::MakePointerAccessor(&TeleportationApp::m_peerQNode),
66 ns3::MakePointerChecker<q2ns::QNode>())
67
68 .AddAttribute("Peer",
69 "Classical control-plane peer address (destination for ctrl packets, used "
70 "by source).",
71 ns3::Ipv4AddressValue(ns3::Ipv4Address("0.0.0.0")),
72 ns3::MakeIpv4AddressAccessor(&TeleportationApp::m_peer),
73 ns3::MakeIpv4AddressChecker())
74
75 .AddAttribute(
76 "CtrlPort", "UDP/TCP port used for teleportation control packets (per-session).",
77 ns3::UintegerValue(7000), ns3::MakeUintegerAccessor(&TeleportationApp::m_ctrlPort),
78 ns3::MakeUintegerChecker<uint16_t>())
79
80 .AddAttribute("ClassicalProtocol", "Control-plane transport protocol: 'udp' or 'tcp'.",
81 ns3::StringValue("udp"),
82 ns3::MakeStringAccessor(&TeleportationApp::m_ctrlProto),
83 ns3::MakeStringChecker())
84
85 .AddAttribute(
86 "Backend", "Quantum state backend to use (e.g., 'ket').", ns3::StringValue("ket"),
87 ns3::MakeStringAccessor(&TeleportationApp::m_backend), ns3::MakeStringChecker())
88
89 .AddAttribute("SessionId", "Unique identifier for the logical teleportation session.",
90 ns3::UintegerValue(0),
91 ns3::MakeUintegerAccessor(&TeleportationApp::m_sessionId),
92 ns3::MakeUintegerChecker<uint64_t>())
93
94 .AddAttribute("TargetQubitTag",
95 "Label used to fetch Bob's target qubit from the NetController.",
96 ns3::StringValue("teleport_target_s"),
97 ns3::MakeStringAccessor(&TeleportationApp::m_targetQubitTag),
98 ns3::MakeStringChecker())
99
100 .AddAttribute("SessionStartTime", "Simulation time at which this app begins its session.",
101 ns3::TimeValue(ns3::Seconds(0.0)),
102 ns3::MakeTimeAccessor(&TeleportationApp::m_sessionStart),
103 ns3::MakeTimeChecker())
104
105 .AddTraceSource("SourceStart", "Alice session start (sid,time)",
106 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSourceStart),
107 "ns3::TracedCallback::Uint64Time")
108
109 .AddTraceSource("SourceBellDone", "Alice Bell measurement done (sid,time)",
110 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSourceBellDone),
111 "ns3::TracedCallback::Uint64Time")
112
113 .AddTraceSource("SourceCtrlSent", "Alice sent ctrl bits (sid,time)",
114 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSourceCtrlSent),
115 "ns3::TracedCallback::Uint64Time")
116
117 .AddTraceSource("SinkStart", "Bob session start (sid,time)",
118 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSinkStart),
119 "ns3::TracedCallback::Uint64Time")
120
121 .AddTraceSource("SinkQArrive", "Bob received his qubit (sid,time)",
122 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSinkQArrive),
123 "ns3::TracedCallback::Uint64Time")
124
125 .AddTraceSource("SinkCtrlArrive", "Bob received ctrl bits (sid,time)",
126 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSinkCtrlArrive),
127 "ns3::TracedCallback::Uint64Time")
128
129 .AddTraceSource("SinkCorrection", "Bob applied correction (sid,time)",
130 ns3::MakeTraceSourceAccessor(&TeleportationApp::m_traceSinkCorrection),
131 "ns3::TracedCallback::Uint64Time");
132
133 return tid;
134}
135
139
140void TeleportationApp::SetTeleportState(const std::shared_ptr<QState>& st) {
141 m_teleportState = st;
142}
143
144
146
148
149 // Ensure the transmitted qubit has a unique id (append once)
150 {
151 const std::string suffix = "_s" + std::to_string(m_sessionId);
152 if (m_targetQubitTag.size() < suffix.size() ||
153 m_targetQubitTag.substr(m_targetQubitTag.size() - suffix.size()) != suffix) {
154 m_targetQubitTag += suffix;
155 }
156 }
157
158 // Defer work to SessionStart to allow staggered starts via Attribute.
159 ns3::Simulator::Schedule(m_sessionStart, [this]() {
160 const auto now = ns3::Simulator::Now().GetSeconds();
161 NS_LOG_INFO("[" << now << "s] " << ns3::Names::FindName(GetNode()) << " TeleportationApp("
162 << m_role << ") start");
163 if (m_role == "source") {
165 } else if (m_role == "sink") {
166 DoSinkStart();
167 } else {
168 NS_LOG_ERROR("TeleportationApp role must be 'source' or 'sink', got: " << m_role);
169 }
170 });
171}
172
174 if (m_ctrlSender) {
175 m_ctrlSender = nullptr;
176 }
177 if (m_ctrlSink) {
178 m_ctrlSink = nullptr;
179 }
180}
181
183
184 m_traceSourceStart(m_sessionId, ns3::Simulator::Now());
185
186 auto qnode = ns3::DynamicCast<q2ns::QNode>(GetNode());
187 if (!qnode) {
188 NS_LOG_WARN("TeleportationApp(source): no QNode bound to this ns-3 node.");
189 return;
190 }
191
192 if (!m_teleportState) {
193 NS_LOG_ERROR(
194 "TeleportationApp(source): no source QState to be teleported provided. Aborting session.");
195 return;
196 }
197
198 auto psi = qnode->CreateQubit(m_teleportState, "teleport_src_s" + std::to_string(m_sessionId));
199
200 auto [aHalf, bHalf] = qnode->CreateBellPair();
201 aHalf->SetLabel("epr_A_s" + std::to_string(m_sessionId));
202 bHalf->SetLabel(m_targetQubitTag);
203
204 bool sent = false;
205 if (m_peerQNode) {
206 sent = qnode->Send(bHalf, m_peerQNode->GetId());
207 } else {
208 NS_LOG_INFO("[" << ns3::Simulator::Now() << "s] " << ns3::Names::FindName(GetNode())
209 << " TeleportationApp(source): no PeerQNode set; qubit cannot be sent.\n");
210 sent = false;
211 }
212 if (!sent) {
213 NS_LOG_WARN("TeleportationApp(source): failed to send EPR half to Bob; proceeding anyway");
214 }
215
216 auto [m1, m2] = qnode->MeasureBell(psi, aHalf);
217 m_traceSourceBellDone(m_sessionId, ns3::Simulator::Now());
218
220}
221
223
224 m_traceSinkStart(m_sessionId, ns3::Simulator::Now());
225
226 auto qnode = ns3::DynamicCast<q2ns::QNode>(GetNode());
227 qnode->SetRecvCallback(MakeCallback(&TeleportationApp::QubitArrivalHandler, this));
228
229 using namespace ns3;
230
231 Address bindAddr = InetSocketAddress(Ipv4Address::GetAny(), m_ctrlPort);
232 if (m_ctrlProto == "udp") {
233 PacketSinkHelper sinkHelper("ns3::UdpSocketFactory", bindAddr);
234 auto apps = sinkHelper.Install(GetNode());
235 m_ctrlSink = DynamicCast<PacketSink>(apps.Get(0));
236 } else {
237 PacketSinkHelper sinkHelper("ns3::TcpSocketFactory", bindAddr);
238 auto apps = sinkHelper.Install(GetNode());
239 m_ctrlSink = DynamicCast<PacketSink>(apps.Get(0));
240 }
241
242 if (m_ctrlSink) {
243 m_ctrlSink->TraceConnectWithoutContext("Rx",
244 ns3::MakeCallback(&TeleportationApp::OnCtrlRx, this));
245 } else {
246 NS_LOG_ERROR("Failed to install PacketSink for ctrl on port " << m_ctrlPort);
247 }
248}
249
250void TeleportationApp::OnTcpConnectSuccess(ns3::Ptr<ns3::Socket> s) {
251 if (m_pendingCtrlPkt) {
252 s->Send(m_pendingCtrlPkt);
253 m_pendingCtrlPkt = nullptr;
254 }
255 s->Close();
256}
257
258void TeleportationApp::OnTcpConnectFail(ns3::Ptr<ns3::Socket> s) {
259 NS_LOG_WARN("TeleportationApp(source): TCP connect failed\n");
260 s->Close();
261}
262
263
264void TeleportationApp::SendCtrlBits(uint8_t m1, uint8_t m2) {
265 using namespace ns3;
266
267
268 m_traceSourceCtrlSent(m_sessionId, ns3::Simulator::Now());
269 NS_LOG_INFO("[" << ns3::Simulator::Now() << "s] " << ns3::Names::FindName(GetNode())
270 << " TeleportationApp(source): Sending classical packet.");
271
272 uint8_t buf[2] = {m1, m2};
273 Ptr<Packet> pkt = Create<Packet>(buf, 2);
274
275 if (m_ctrlProto == "udp") {
276 Ptr<Socket> sock = Socket::CreateSocket(GetNode(), UdpSocketFactory::GetTypeId());
277 InetSocketAddress dst(m_peer, m_ctrlPort);
278 if (sock->Connect(dst) == 0) {
279 sock->Send(pkt);
280 } else {
281 NS_LOG_WARN("TeleportationApp(source): UDP connect failed to " << m_peer << ":" << m_ctrlPort
282 << "\n");
283 }
284 sock->Close();
285 return;
286 }
287
288 Ptr<Socket> sock = Socket::CreateSocket(GetNode(), TcpSocketFactory::GetTypeId());
289 InetSocketAddress dst(m_peer, m_ctrlPort);
290
291 // Save the packet; send it when the TCP handshake completes.
292 m_pendingCtrlPkt = pkt;
293
294 sock->SetConnectCallback(ns3::MakeCallback(&TeleportationApp::OnTcpConnectSuccess, this),
295 ns3::MakeCallback(&TeleportationApp::OnTcpConnectFail, this));
296
297 int rc = sock->Connect(dst);
298 if (rc != 0) {
299 NS_LOG_WARN("TeleportationApp(source): TCP connect() immediate failure to "
300 << m_peer << ":" << m_ctrlPort << "\n");
301 sock->Close();
302 m_pendingCtrlPkt = nullptr;
303 }
304}
305
306void TeleportationApp::QubitArrivalHandler(std::shared_ptr<Qubit> q) {
307
308 m_traceSinkQArrive(m_sessionId, ns3::Simulator::Now());
309 NS_LOG_INFO("[" << ns3::Simulator::Now() << "s] " << ns3::Names::FindName(GetNode())
310 << " TeleportationApp(sink): Received qubit " + q->GetLabel() + ".");
311
312 m_haveQ = true;
314}
315
316void TeleportationApp::OnCtrlRx(ns3::Ptr<const ns3::Packet> pkt, const ns3::Address& /*from*/) {
317 using namespace ns3;
318
319 m_traceSinkCtrlArrive(m_sessionId, ns3::Simulator::Now());
320 NS_LOG_INFO("[" << ns3::Simulator::Now() << "s] " << ns3::Names::FindName(GetNode())
321 << " TeleportationApp(sink): Received classical packet.");
322
323 uint8_t bits[2] = {0, 0};
324 if (!pkt || pkt->GetSize() < 2) {
325 NS_LOG_WARN("TeleportationApp(sink): control packet too small (" << (pkt ? pkt->GetSize() : 0)
326 << " bytes)\n");
327 return;
328 }
329 pkt->CopyData(bits, 2);
330 m_m1 = bits[0];
331 m_m2 = bits[1];
332 m_haveCtrl = true;
333
334
336}
337
339 if (!(m_haveCtrl && m_haveQ))
340 return;
341
342 if (auto qnode = ns3::DynamicCast<q2ns::QNode>(GetNode())) {
343 auto qB = qnode->GetQubit(m_targetQubitTag);
344 if (qB) {
345 if (m_m2)
346 qnode->Apply(q2ns::gates::X(), {qB});
347 if (m_m1)
348 qnode->Apply(q2ns::gates::Z(), {qB});
349
350 m_traceSinkCorrection(m_sessionId, ns3::Simulator::Now());
351 } else {
352 // We thought we had Q, but label not found -- keep waiting.
353 return;
354 }
355 }
356}
357
358
359} // namespace q2ns
Main user-facing facade for creating and configuring a quantum network.
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSourceBellDone
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSourceCtrlSent
void OnTcpConnectSuccess(ns3::Ptr< ns3::Socket > s)
ns3::Ptr< ns3::Application > m_ctrlSender
ns3::Ptr< ns3::PacketSink > m_ctrlSink
void SetTeleportState(const std::shared_ptr< QState > &state)
q2ns::NetController * m_nc
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSinkCorrection
void QubitArrivalHandler(std::shared_ptr< Qubit > q)
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSourceStart
void OnTcpConnectFail(ns3::Ptr< ns3::Socket > s)
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSinkStart
ns3::Ptr< ns3::Packet > m_pendingCtrlPkt
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSinkQArrive
static ns3::TypeId GetTypeId()
void SetNetController(NetController *nc)
void OnCtrlRx(ns3::Ptr< const ns3::Packet > pkt, const ns3::Address &from)
std::shared_ptr< QState > m_teleportState
ns3::Ptr< q2ns::QNode > m_peerQNode
ns3::TracedCallback< uint64_t, ns3::Time > m_traceSinkCtrlArrive
void SendCtrlBits(uint8_t m1, uint8_t m2)
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
uint8_t m2
uint8_t m1