Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qmap.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-qmap.cc
11 * @brief Defines q2ns::QMap and standard channel-map implementations.
12 */
13
14#include "ns3/q2ns-qmap.h"
15
16#include "ns3/q2ns-qgate.h"
17#include "ns3/q2ns-qnode.h"
18#include "ns3/q2ns-qubit.h"
19
20#include "ns3/attribute.h"
21#include "ns3/double.h"
22#include "ns3/integer.h"
23#include "ns3/log.h"
24#include "ns3/string.h"
25
26#include <cctype>
27#include <utility>
28
29namespace q2ns {
30
31NS_LOG_COMPONENT_DEFINE("QMap");
32
33NS_OBJECT_ENSURE_REGISTERED(QMap);
34
35ns3::TypeId QMap::GetTypeId() {
36 static ns3::TypeId tid =
37 ns3::TypeId("q2ns::QMap")
38 .SetParent<ns3::Object>()
39 .SetGroupName("q2ns")
40 .AddAttribute("Probability",
41 "Base application probability used when Rate is 0. "
42 "If Rate > 0, Probability is ignored.",
43 ns3::DoubleValue(0.0), ns3::MakeDoubleAccessor(&QMap::p_),
44 ns3::MakeDoubleChecker<double>(0.0, 1.0))
45 .AddAttribute("Rate",
46 "QMap event rate in 1/s. If > 0, the per-flight "
47 "application probability is p(t)=1-exp(-Rate*elapsedTime).",
48 ns3::DoubleValue(0.0), ns3::MakeDoubleAccessor(&QMap::rate_),
49 ns3::MakeDoubleChecker<double>(0.0));
50 return tid;
51}
52
53
54
57}
58
59namespace {
60
61/**
62 * @class CompositeQMap
63 * @brief Internal QMap implementation for sequential composition.
64 *
65 * This type is intentionally hidden from users. It samples each component map
66 * once for the current transmission and later executes the sampled instances
67 * left to right at the receiver.
68 */
69class CompositeQMap final : public QMap {
70public:
71 static ns3::TypeId GetTypeId() {
72 static ns3::TypeId tid = ns3::TypeId("q2ns::CompositeQMap")
73 .SetParent<QMap>()
74 .SetGroupName("q2ns")
75 .AddConstructor<CompositeQMap>();
76 return tid;
77 }
78
79 CompositeQMap() = default;
80
81
82
83 void SetMaps(const std::vector<ns3::Ptr<QMap>>& maps) {
84 maps_ = maps;
85 }
86
87
88
89 QMapInstance Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
90 const QMapContext& ctx = QMapContext{}) const override {
91 std::vector<QMapInstance> inst;
92 inst.reserve(maps_.size());
93 for (const auto& m : maps_) {
94 inst.push_back(m ? m->Sample(u, ctx) : QMapInstance{});
95 }
96
97 return [inst = std::move(inst)](QNode& node, std::shared_ptr<Qubit>& q) mutable {
98 for (auto& f : inst) {
99 if (f) {
100 f(node, q);
101 }
102
103 // Stop immediately if one stage marks the qubit lost.
104 auto loc = q->GetLocation();
105 if (loc.type == LocationType::Lost) {
106 return;
107 }
108 }
109 };
110 }
111
112private:
113 std::vector<ns3::Ptr<QMap>> maps_; //!< Component maps in left-to-right order.
114};
115
116NS_OBJECT_ENSURE_REGISTERED(CompositeQMap);
117
118} // namespace
119
120
121
122ns3::Ptr<QMap> QMap::Compose(const ns3::Ptr<QMap>& a, const ns3::Ptr<QMap>& b) {
123 std::vector<ns3::Ptr<QMap>> v;
124 v.reserve(2);
125 v.push_back(a);
126 v.push_back(b);
127 return Compose(v);
128}
129
130
131
132ns3::Ptr<QMap> QMap::Compose(const std::vector<ns3::Ptr<QMap>>& maps) {
133 auto out = ns3::CreateObject<CompositeQMap>();
134 out->SetMaps(maps);
135 return out;
136}
137
138
139
140ns3::Ptr<QMap> QMap::FromLambda(std::function<void(QNode&, std::shared_ptr<Qubit>&)> f) {
141 return ns3::CreateObject<LambdaQMap>(std::move(f));
142}
143
144
145
146ns3::Ptr<QMap>
147QMap::FromLambda(std::function<void(QNode&, std::shared_ptr<Qubit>&,
148 ns3::Ptr<ns3::UniformRandomVariable>, const QMapContext&)>
149 f) {
150 return ns3::CreateObject<LambdaQMap>(std::move(f));
151}
152
153
154
155NS_OBJECT_ENSURE_REGISTERED(LambdaQMap);
156
158 static ns3::TypeId tid = ns3::TypeId("q2ns::LambdaQMap")
159 .SetParent<QMap>()
160 .SetGroupName("q2ns")
161 .AddConstructor<LambdaQMap>();
162 return tid;
163}
164
165
166
167LambdaQMap::LambdaQMap(SimpleFn f) : simple_(std::move(f)) {}
168
169
170
171LambdaQMap::LambdaQMap(AdvancedFn f) : advanced_(std::move(f)) {}
172
173
174
176 simple_ = std::move(f);
178}
179
180
181
183 advanced_ = std::move(f);
184 simple_ = SimpleFn{};
185}
186
187
188
189QMapInstance LambdaQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
190 const QMapContext& ctx) const {
191 // Prefer the advanced callable when present so the caller can use the sampled
192 // context and RNG source directly.
193 if (advanced_) {
194 auto adv = advanced_;
195 return [adv, u, ctx](QNode& node, std::shared_ptr<Qubit>& q) mutable { adv(node, q, u, ctx); };
196 }
197
198 if (simple_) {
199 auto simp = simple_;
200 return [simp](QNode& node, std::shared_ptr<Qubit>& q) mutable { simp(node, q); };
201 }
202
203 return QMapInstance{};
204}
205
206
207
208NS_OBJECT_ENSURE_REGISTERED(ConditionalQMap);
209
211 static ns3::TypeId tid = ns3::TypeId("q2ns::ConditionalQMap")
212 .SetParent<QMap>()
213 .SetGroupName("q2ns")
214 .AddConstructor<ConditionalQMap>();
215 return tid;
216}
217
218
219
220void ConditionalQMap::SetQMap(ns3::Ptr<QMap> qmap) {
221 qmap_ = qmap;
222}
223
224
225
227 cond_ = std::move(pred);
228}
229
230
231
232QMapInstance ConditionalQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
233 const QMapContext& ctx) const {
234 const auto pred = cond_;
235 QMapInstance qmapInst = qmap_ ? qmap_->Sample(u, ctx) : QMapInstance{};
236
237 return [pred, ctx, qmapInst](QNode& node, std::shared_ptr<Qubit>& q) mutable {
238 if (!qmapInst) {
239 return;
240 }
241 if (!pred || pred(q, ctx)) {
242 qmapInst(node, q);
243 }
244 };
245}
246
247
248
249NS_OBJECT_ENSURE_REGISTERED(DephasingQMap);
250
252 static ns3::TypeId tid = ns3::TypeId("q2ns::DephasingQMap")
253 .SetParent<QMap>()
254 .SetGroupName("q2ns")
255 .AddConstructor<DephasingQMap>();
256 return tid;
257}
258
259
260
261QMapInstance DephasingQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
262 const QMapContext& ctx) const {
263 const bool flip = Bernoulli_(u, ctx);
264 return [flip](QNode& node, std::shared_ptr<Qubit>& q) {
265 if (flip) {
266 node.Apply(q2ns::gates::Z(), {q});
267 }
268 };
269}
270
271
272
273NS_OBJECT_ENSURE_REGISTERED(DepolarizingQMap);
274
276 static ns3::TypeId tid = ns3::TypeId("q2ns::DepolarizingQMap")
277 .SetParent<QMap>()
278 .SetGroupName("q2ns")
279 .AddConstructor<DepolarizingQMap>();
280 return tid;
281}
282
283
284
285QMapInstance DepolarizingQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
286 const QMapContext& ctx) const {
287 const bool apply = Bernoulli_(u, ctx);
288
289 uint32_t r = 0;
290 if (apply) {
291 r = u->GetInteger(0, 2);
292 }
293
294 return [apply, r](QNode& node, std::shared_ptr<Qubit>& q) {
295 if (!apply) {
296 return;
297 }
298 if (r == 0) {
299 node.Apply(q2ns::gates::X(), {q});
300 } else if (r == 1) {
301 node.Apply(q2ns::gates::Y(), {q});
302 } else {
303 node.Apply(q2ns::gates::Z(), {q});
304 }
305 };
306}
307
308
309
310NS_OBJECT_ENSURE_REGISTERED(LossQMap);
311
312ns3::TypeId LossQMap::GetTypeId() {
313 static ns3::TypeId tid = ns3::TypeId("q2ns::LossQMap")
314 .SetParent<QMap>()
315 .SetGroupName("q2ns")
316 .AddConstructor<LossQMap>();
317 return tid;
318}
319
320
321
322QMapInstance LossQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
323 const QMapContext& ctx) const {
324 const bool lose = Bernoulli_(u, ctx);
325 return [lose](QNode&, std::shared_ptr<Qubit>& q) {
326 if (lose) {
327 SetLost_(*q);
328 }
329 };
330}
331
332
333
334NS_OBJECT_ENSURE_REGISTERED(RandomGateQMap);
335
337 static ns3::TypeId tid = ns3::TypeId("q2ns::RandomGateQMap")
338 .SetParent<QMap>()
339 .SetGroupName("q2ns")
340 .AddConstructor<RandomGateQMap>();
341 return tid;
342}
343
344
345
347 gates_.clear();
348 weights_.clear();
349 totalWeight_ = 0.0;
350}
351
352
353
354void RandomGateQMap::AddGate(const QGate& gate, double weight) {
355 NS_ABORT_MSG_IF(weight < 0.0, "RandomGateQMap: weight must be >= 0");
356 gates_.push_back(gate);
357 weights_.push_back(weight);
358 totalWeight_ += weight;
359}
360
361
362
363void RandomGateQMap::SetDistribution(std::vector<QGate> gates, std::vector<double> weights) {
364 NS_ABORT_MSG_IF(gates.size() != weights.size(),
365 "RandomGateQMap: gates and weights must have the same length");
366
367 gates_ = std::move(gates);
368 weights_ = std::move(weights);
369
370 totalWeight_ = 0.0;
371 for (double w : weights_) {
372 NS_ABORT_MSG_IF(w < 0.0, "RandomGateQMap: weight must be >= 0");
373 totalWeight_ += w;
374 }
375}
376
377
378
379std::size_t RandomGateQMap::PickIndex_(ns3::Ptr<ns3::UniformRandomVariable> u) const {
380 NS_ABORT_MSG_IF(gates_.empty(), "RandomGateQMap: no gates configured");
381 NS_ABORT_MSG_IF(totalWeight_ <= 0.0, "RandomGateQMap: total weight must be > 0");
382
383 const double r = u->GetValue(0.0, totalWeight_);
384 double acc = 0.0;
385 for (std::size_t i = 0; i < weights_.size(); ++i) {
386 acc += weights_[i];
387 if (r <= acc) {
388 return i;
389 }
390 }
391 return weights_.size() - 1;
392}
393
394
395
396QMapInstance RandomGateQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
397 const QMapContext& ctx) const {
398 const bool apply = Bernoulli_(u, ctx);
399
400 std::size_t idx = 0;
401 QGate gate;
402 if (apply) {
403 idx = PickIndex_(u);
404 gate = gates_[idx];
405 }
406
407 return [apply, gate = std::move(gate)](QNode& node, std::shared_ptr<Qubit>& q) mutable {
408 if (apply) {
409 node.Apply(gate, {q});
410 }
411 };
412}
413
414
415
416NS_OBJECT_ENSURE_REGISTERED(RandomUnitaryQMap);
417
418ns3::TypeId RandomUnitaryQMap::GetTypeId() {
419 static ns3::TypeId tid = ns3::TypeId("q2ns::RandomUnitaryQMap")
420 .SetParent<QMap>()
421 .SetGroupName("q2ns")
422 .AddConstructor<RandomUnitaryQMap>();
423 return tid;
424}
425
426
427
428// Haar-random SU(2) from a random unit quaternion on S^3.
429// Converted to the 2x2 unitary
430// [ u0 + i u3 u2 + i u1 ]
431// [ -u2 + i u1 u0 - i u3 ].
432Matrix RandomUnitaryQMap::SampleHaarSU2_(ns3::Ptr<ns3::UniformRandomVariable> u) {
433 const double a = u->GetValue(0.0, 1.0);
434 const double b = u->GetValue(0.0, 1.0);
435 const double c = u->GetValue(0.0, 1.0);
436
437 const double s1 = std::sqrt(1.0 - a);
438 const double s2 = std::sqrt(a);
439 constexpr double kTwoPi = 6.28318530717958647692;
440 const double t1 = kTwoPi * b;
441 const double t2 = kTwoPi * c;
442
443 const double u0 = s2 * std::cos(t2);
444 const double u3 = s2 * std::sin(t2);
445 const double u2 = s1 * std::cos(t1);
446 const double u1 = s1 * std::sin(t1);
447
448 const Complex A(u0, u3);
449 const Complex B(u2, u1);
450 const Complex C(-u2, u1);
451 const Complex D(u0, -u3);
452
453 return MakeMatrix({{A, B}, {C, D}});
454}
455
456
457
458QMapInstance RandomUnitaryQMap::Sample(ns3::Ptr<ns3::UniformRandomVariable> u,
459 const QMapContext& ctx) const {
460 const bool apply = Bernoulli_(u, ctx);
461
462 Matrix U;
463 if (apply) {
464 U = SampleHaarSU2_(u);
465 }
466
467 return [apply, U = std::move(U)](QNode& node, std::shared_ptr<Qubit>& q) mutable {
468 if (apply) {
469 node.Apply(U, {q});
470 }
471 };
472}
473
474} // namespace q2ns
QMap wrapper that conditionally applies another QMap.
Definition q2ns-qmap.h:287
void SetQMap(ns3::Ptr< QMap > qmap)
Set the wrapped QMap.
Definition q2ns-qmap.cc:220
QMapInstance Sample(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx=QMapContext{}) const override
Sample a per-transmission QMapInstance.
Definition q2ns-qmap.cc:232
Condition cond_
Application predicate.
Definition q2ns-qmap.h:323
std::function< bool(const std::shared_ptr< Qubit > &, const QMapContext &)> Condition
Predicate type controlling whether the wrapped map is applied.
Definition q2ns-qmap.h:292
void SetCondition(Condition pred)
Set the condition predicate.
Definition q2ns-qmap.cc:226
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:210
ns3::Ptr< QMap > qmap_
Wrapped QMap.
Definition q2ns-qmap.h:322
Dephasing noise model that applies Z with probability p.
Definition q2ns-qmap.h:331
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:251
QMapInstance Sample(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx=QMapContext{}) const override
Sample a per-transmission QMapInstance.
Definition q2ns-qmap.cc:261
Trajectory-style depolarizing model that applies a random Pauli from {X, Y, Z} with probability p.
Definition q2ns-qmap.h:355
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:275
QMapInstance Sample(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx=QMapContext{}) const override
Sample a per-transmission QMapInstance.
Definition q2ns-qmap.cc:285
QMap implementation that wraps a user-provided lambda.
Definition q2ns-qmap.h:217
AdvancedFn advanced_
Advanced callable, if configured.
Definition q2ns-qmap.h:276
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:157
void Set(SimpleFn f)
Replace the stored callable with a simple callable.
Definition q2ns-qmap.cc:175
QMapInstance Sample(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx=QMapContext{}) const override
Sample a per-transmission QMapInstance.
Definition q2ns-qmap.cc:189
std::function< void(QNode &, std::shared_ptr< Qubit > &, ns3::Ptr< ns3::UniformRandomVariable >, const QMapContext &)> AdvancedFn
Advanced callable type.
Definition q2ns-qmap.h:228
LambdaQMap()=default
Default constructor.
std::function< void(QNode &, std::shared_ptr< Qubit > &)> SimpleFn
Simple callable type.
Definition q2ns-qmap.h:222
SimpleFn simple_
Simple callable, if configured.
Definition q2ns-qmap.h:275
Erasure model that marks the qubit lost with probability p.
Definition q2ns-qmap.h:378
QMapInstance Sample(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx=QMapContext{}) const override
Sample a per-transmission QMapInstance.
Definition q2ns-qmap.cc:322
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:312
Lightweight gate descriptor used by QState backends.
Definition q2ns-qgate.h:68
Abstract base class for channel map models.
Definition q2ns-qmap.h:65
static ns3::Ptr< QMap > Compose(const ns3::Ptr< QMap > &a, const ns3::Ptr< QMap > &b)
Compose two QMaps into one sequential composite QMap.
Definition q2ns-qmap.cc:122
static void SetLost_(Qubit &q)
Mark a qubit lost through the standard registry-backed location path.
Definition q2ns-qmap.cc:55
static ns3::Ptr< QMap > FromLambda(std::function< void(QNode &, std::shared_ptr< Qubit > &)> f)
Build a QMap from a simple lambda.
Definition q2ns-qmap.cc:140
double p_
Direct per-transmission probability.
Definition q2ns-qmap.h:200
double rate_
Poisson event rate in 1/s. Overrides p_ when positive.
Definition q2ns-qmap.h:201
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:35
bool Bernoulli_(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx) const
Perform one Bernoulli trial using the effective probability.
Definition q2ns-qmap.h:183
Main user-facing per-node API for quantum operations and transmission.
Definition q2ns-qnode.h:63
bool Apply(const QGate &gate, const std::vector< std::shared_ptr< Qubit > > &qs)
Apply a gate to one or more local qubits.
Lightweight handle for one qubit inside a registry-managed state.
Definition q2ns-qubit.h:48
void SetLocationLost()
Mark this qubit as lost.
QMap that samples one gate from a weighted distribution and applies it.
Definition q2ns-qmap.h:404
static ns3::TypeId GetTypeId()
Get the ns-3 TypeId.
Definition q2ns-qmap.cc:336
void AddGate(const QGate &gate, double weight)
Append one weighted gate to the distribution.
Definition q2ns-qmap.cc:354
double totalWeight_
Sum of all selection weights.
Definition q2ns-qmap.h:450
void Clear()
Clear the weighted gate distribution.
Definition q2ns-qmap.cc:346
std::vector< QGate > gates_
Candidate gates.
Definition q2ns-qmap.h:448
std::vector< double > weights_
Selection weights.
Definition q2ns-qmap.h:449
QMap that applies one Haar-random single-qubit SU(2) unitary.
Definition q2ns-qmap.h:461
Internal QMap implementation for sequential composition.
Definition q2ns-qmap.cc:69
QMapInstance Sample(ns3::Ptr< ns3::UniformRandomVariable > u, const QMapContext &ctx=QMapContext{}) const override
Sample a per-transmission QMapInstance.
Definition q2ns-qmap.cc:89
void SetMaps(const std::vector< ns3::Ptr< QMap > > &maps)
Definition q2ns-qmap.cc:83
std::vector< ns3::Ptr< QMap > > maps_
Component maps in left-to-right order.
Definition q2ns-qmap.cc:113
std::function< void(QNode &, std::shared_ptr< Qubit > &)> QMapInstance
Per-transmission quantum map callable applied to a received qubit.
Definition q2ns-types.h:71
QGate Y(ns3::Time d=ns3::Seconds(0))
Return the Pauli-Y gate descriptor.
Definition q2ns-qgate.h:365
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
Optional per-sample context passed to QMaps.
Definition q2ns-qmap.h:41