Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qprocessor.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-qprocessor.cc
11 * @brief Defines q2ns::QProcessor.
12 */
13
14#include "q2ns-qprocessor.h"
15
16#include "ns3/q2ns-qnode.h"
17#include "ns3/q2ns-qstate-registry.h"
18#include "ns3/q2ns-qstate.h"
19#include "ns3/q2ns-qubit.h"
20
21#include "ns3/log.h"
22
23#include <algorithm>
24
25namespace q2ns {
26
27NS_LOG_COMPONENT_DEFINE("QProcessor");
28
30 : registry_(registry), owner_(owner) {}
31
32
33
35 return owner_;
36}
37
38
39
40std::shared_ptr<Qubit> QProcessor::CreateQubit(const std::string& label) {
41
42 if (!label.empty() && GetQubit(label)) {
43 NS_LOG_WARN(
44 "CreateQubit received a duplicate local label '"
45 << label
46 << "'. A new qubit will still be created, but label-based lookup may be ambiguous.");
47 }
48
49 auto res = registry_.CreateState(1);
50 auto q = std::make_shared<Qubit>(registry_, res.stateId, res.indices[0], label);
51
53 q->SetLocationNode(owner_.GetId());
54
55 return q;
56}
57
58
59
60std::shared_ptr<Qubit> QProcessor::CreateQubit(const std::shared_ptr<QState>& state,
61 const std::string& label) {
62 if (!state) {
63 NS_LOG_WARN("CreateQubit rejected: null state.");
64 return nullptr;
65 }
66
67 if (!label.empty() && GetQubit(label)) {
68 NS_LOG_WARN("CreateQubit received a duplicate local label '"
69 << label << "'. Label-based lookup may be ambiguous.");
70 }
71
72 auto res = registry_.CreateStateFromExisting(state);
73 auto q = std::make_shared<Qubit>(registry_, res.stateId, res.indices[0], label);
74
76 q->SetLocationNode(owner_.GetId());
77
78 return q;
79}
80
81
82
83std::pair<std::shared_ptr<Qubit>, std::shared_ptr<Qubit>> QProcessor::CreateBellPair() {
84 auto created = registry_.CreateState(2);
85 auto q0 = std::make_shared<Qubit>(registry_, created.stateId, created.indices[0], "bell0");
86 auto q1 = std::make_shared<Qubit>(registry_, created.stateId, created.indices[1], "bell1");
87
90 q0->SetLocationNode(owner_.GetId());
91 q1->SetLocationNode(owner_.GetId());
92
93 Apply(q2ns::gates::H(), {q0});
94 Apply(q2ns::gates::CNOT(), {q0, q1});
95
96 return {q0, q1};
97}
98
99
100
101std::shared_ptr<Qubit> QProcessor::GetQubit(const std::string& label) const {
102 if (label.empty()) {
103 NS_LOG_WARN("GetQubit failed: label is empty.");
104 return nullptr;
105 }
106
107 for (const auto& q : registry_.GetLocalQubits(owner_.GetId())) {
108 if (q && q->GetLabel() == label) {
109 return q;
110 }
111 }
112
113 NS_LOG_WARN("GetQubit failed: no qubit found in local qubits with label=" << label << ".");
114 return nullptr;
115}
116
117
118
119std::shared_ptr<Qubit> QProcessor::GetQubit(QubitId id) const {
120 auto q = registry_.GetQubitHandle(id);
121 if (!q) {
122 NS_LOG_WARN("GetQubit failed: no matching qubit.");
123 return nullptr;
124 }
125
126 const auto loc = registry_.GetLocation(id);
127 if (loc.type == LocationType::Node && loc.ownerId == owner_.GetId()) {
128 return q;
129 }
130
131 NS_LOG_WARN("GetQubit failed: no qubit found in local qubits with id=" << id << ".");
132 return nullptr;
133}
134
135
136
137std::vector<std::shared_ptr<Qubit>> QProcessor::GetLocalQubits() const {
138 return registry_.GetLocalQubits(owner_.GetId());
139}
140
141
142
143void QProcessor::AdoptQubit(const std::shared_ptr<Qubit>& q) {
144 if (!q) {
145 NS_LOG_WARN("AdoptQubit rejected: null qubit.");
146 return;
147 }
148
149 const auto loc = q->GetLocation();
150 if (loc.type == LocationType::Lost) {
151 NS_LOG_WARN("AdoptQubit rejected: qubit is lost.");
152 return;
153 }
154
155 q->SetLocationNode(owner_.GetId());
156}
157
158
159
160std::shared_ptr<QState> QProcessor::GetState(const std::shared_ptr<Qubit>& q) const {
161 if (!q) {
162 NS_LOG_WARN("GetState rejected: null qubit.");
163 return nullptr;
164 }
165 return registry_.GetState(q);
166}
167
168
169
170bool QProcessor::Apply(const QGate& gate, const std::vector<std::shared_ptr<Qubit>>& qs) {
171 if (qs.empty()) {
172 NS_LOG_WARN("Apply rejected: empty qubit list.");
173 return false;
174 }
175
176 for (const auto& q : qs) {
177 if (!q) {
178 NS_LOG_WARN("Apply rejected: null qubit in target list.");
179 return false;
180 }
181
182 const auto locQ = registry_.GetLocation(q);
183 if (!(locQ.type == LocationType::Node && locQ.ownerId == owner_.GetId())) {
184 NS_LOG_WARN("Apply rejected: target qubit is not local to this node.");
185 return false;
186 }
187 }
188
189 // Merge target states if needed so the backend gate can be applied using a
190 // single state object and the current post-merge qubit indices.
191 auto st = registry_.MergeStates(qs);
192 if (!st) {
193 NS_LOG_WARN("Apply failed: state merge did not produce a valid backend state.");
194 return false;
195 }
196
197 std::vector<q2ns::Index> targets;
198 targets.reserve(qs.size());
199 for (const auto& q : qs) {
200 targets.push_back(q->GetIndexInState());
201 }
202
203 st->Apply(gate, targets);
204 return true;
205}
206
207
208
209int QProcessor::Measure(const std::shared_ptr<Qubit>& q, q2ns::Basis basis) {
210 if (!q) {
211 NS_LOG_WARN("Measure rejected: null qubit.");
212 return -1;
213 }
214
215 const auto locQ = registry_.GetLocation(q);
216 if (!(locQ.type == LocationType::Node && locQ.ownerId == owner_.GetId())) {
217 NS_LOG_WARN("Measure rejected: qubit is not local to this node.");
218 return -1;
219 }
220
221 auto currentState = registry_.GetState(q->GetStateId());
222 if (!currentState) {
223 NS_LOG_WARN("Measure failed: could not retrieve the qubit's current state.");
224 return -1;
225 }
226
227 auto related = registry_.QubitsOf(currentState->GetStateId());
228
229 // Sort by current state index before rebinding. After measurement removes
230 // one qubit, survivor indices are compacted and must be reassigned in order.
231 std::sort(related.begin(), related.end(), [](const auto& a, const auto& b) {
232 return a->GetIndexInState() < b->GetIndexInState();
233 });
234
235 const auto removedIndex = q->GetIndexInState();
236 auto split = currentState->Measure(removedIndex, basis);
237 const int result = static_cast<int>(split.outcome);
238
239
240 // Unregister all related qubits from the old state before rebinding them to the measured and
241 // survivor states created below.
242 for (const auto& r : related) {
244 }
245
246 // 1) Register the measured 1-qubit state
247 auto newMeasured = registry_.CreateStateFromExisting(split.measured);
248
249 // Rebind 'q' to the measured state (index 0) and restore location (value already retrieved above
250 // during initial checks)
251 q->SetStateId(newMeasured.stateId);
252 q->SetIndexInState(newMeasured.indices[0]);
254 registry_.SetLocation(q, locQ);
255
256
257 // 2) If survivors exist, register them and rebind remaining qubits
258 if (split.survivors && split.survivors->NumQubits() > 0) {
259 auto survivorsRes = registry_.CreateStateFromExisting(split.survivors);
260
261 // Rebind: all original related except 'q' (whose index was removed)
262 unsigned int nextIdx = 0;
263 for (const auto& r : related) {
264 if (r.get() == q.get()) {
265 continue; // skip the measured qubit; already bound above
266 }
267
268 // Preserve location if available
269 auto l = registry_.GetLocation(r);
270
271 r->SetStateId(survivorsRes.stateId);
272 r->SetIndexInState(survivorsRes.indices[nextIdx++]);
275 }
276 }
277
278 // Cleanup the old state if it's empty
279 auto remaining = registry_.QubitsOf(currentState->GetStateId());
280 if (remaining.empty()) {
281 registry_.RemoveState(currentState->GetStateId());
282 }
283
284 return result;
285}
286
287
288
289std::pair<int, int> QProcessor::MeasureBell(const std::shared_ptr<Qubit>& a,
290 const std::shared_ptr<Qubit>& b) {
291 if (!a || !b) {
292 NS_LOG_WARN("MeasureBell rejected: null qubit.");
293 return {-1, -1};
294 }
295
296 const auto locA = a->GetLocation();
297 if (locA.type == LocationType::Lost) {
298 NS_LOG_WARN("MeasureBell rejected: first qubit " << a->GetQubitId() << " is lost.");
299 return {-1, -1};
300 }
301
302 const auto locB = b->GetLocation();
303 if (locB.type == LocationType::Lost) {
304 NS_LOG_WARN("MeasureBell rejected: second qubit " << b->GetQubitId() << " is lost.");
305 return {-1, -1};
306 }
307
308 const auto locA2 = registry_.GetLocation(a);
309 const auto locB2 = registry_.GetLocation(b);
310 if (!(locA2.type == LocationType::Node && locA2.ownerId == owner_.GetId()) ||
311 !(locB2.type == LocationType::Node && locB2.ownerId == owner_.GetId())) {
312 NS_LOG_WARN("MeasureBell rejected: qubits are not both local to this node.");
313 return {-1, -1};
314 }
315
316 // Bell-basis measurement implemented as a basis rotation followed by two
317 // single-qubit measurements in the computational basis.
318 if (!Apply(q2ns::gates::CNOT(), {a, b})) {
319 return {-1, -1};
320 }
321 if (!Apply(q2ns::gates::H(), {a})) {
322 return {-1, -1};
323 }
324
325 auto resultA = Measure(a);
326 auto resultB = Measure(b);
327
328 return {resultA, resultB};
329}
330} // namespace q2ns
Lightweight gate descriptor used by QState backends.
Definition q2ns-qgate.h:68
Main user-facing per-node API for quantum operations and transmission.
Definition q2ns-qnode.h:63
int Measure(const std::shared_ptr< Qubit > &qubit, q2ns::Basis basis=q2ns::Basis::Z)
Measure a single, local qubit in the given basis (default Z).
std::pair< int, int > MeasureBell(const std::shared_ptr< Qubit > &a, const std::shared_ptr< Qubit > &b)
Perform a Bell-state measurement on two local qubits.
void AdoptQubit(const std::shared_ptr< Qubit > &q)
Adopt a qubit into this processor's owning node.
QProcessor(QStateRegistry &registry, QNode &owner)
Construct a processor bound to a registry and owning node.
std::shared_ptr< QState > GetState(const std::shared_ptr< Qubit > &q) const
Get the current state of the qubit.
std::pair< std::shared_ptr< Qubit >, std::shared_ptr< Qubit > > CreateBellPair()
Create a local Bell pair in |Phi+>
QNode & owner_
Owning QNode.
std::shared_ptr< Qubit > GetQubit(const std::string &label) const
Lookup a local qubit by application label.
const QNode & GetOwnerNode() const
Get owning node.
std::shared_ptr< Qubit > CreateQubit(const std::string &label="")
Create a new local qubit in |0>.
bool Apply(const QGate &gate, const std::vector< std::shared_ptr< Qubit > > &qs)
Apply a gate to one or more local qubits.
QStateRegistry & registry_
Quantum state registry.
std::vector< std::shared_ptr< Qubit > > GetLocalQubits() const
Return the qubits currently located at the owning node.
Internal registry that owns backend states and tracks qubit membership and location.
void RemoveState(StateId id)
Remove a backend state and its membership tracking.
void Register(const std::shared_ptr< Qubit > &q)
Register a qubit handle as a member of its current state.
void SetLocation(const std::shared_ptr< Qubit > &q, Location loc)
Set the tracked location for a qubit handle.
CreateStateResult CreateState(unsigned int n)
Create a new state with n qubits initialized in the |0...0> state.
std::shared_ptr< QState > GetState(StateId stateId) const
Get a backend state by state id.
void UnregisterEverywhere(const std::shared_ptr< Qubit > &q)
Remove a qubit handle from membership lists across all states.
std::vector< std::shared_ptr< Qubit > > GetLocalQubits(uint32_t nodeId) const
Return qubit handles currently located at a given node.
CreateStateResult CreateStateFromExisting(const std::shared_ptr< QState > &state)
Register an already-constructed backend state object.
std::shared_ptr< Qubit > GetQubitHandle(QubitId id) const
Return a tracked qubit handle by qubit id.
Location GetLocation(const std::shared_ptr< Qubit > &q) const
Get the tracked location for a qubit handle.
std::shared_ptr< QState > MergeStates(const std::vector< std::shared_ptr< Qubit > > &qs)
Ensure the provided qubits belong to a single backend state.
std::vector< std::shared_ptr< Qubit > > QubitsOf(StateId stateId) const
Return qubit handles that currently belong to a given state.
std::uint64_t QubitId
Stable identifier for a registered qubit handle.
Definition q2ns-types.h:61
Basis
Measurement basis for single-qubit projective measurement.
Definition q2ns-types.h:88
@ Node
Qubit is local to a node identified by node id.
@ Lost
Qubit was lost and is no longer accessible.
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
Declares q2ns::QProcessor, a per-node processor that handles local quantum state operations.