Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qstate-registry.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-qstate-registry.cc
11 * @brief Defines q2ns::QStateRegistry.
12 */
13
14#include "ns3/q2ns-qstate-registry.h"
15
16#include "ns3/q2ns-qstate-all.h"
17#include "ns3/q2ns-qubit.h"
18
19#include "ns3/log.h"
20
21#include <algorithm>
22#include <unordered_set>
23#include <vector>
24
25namespace q2ns {
26
27NS_LOG_COMPONENT_DEFINE("QStateRegistry");
28
29QStateBackend BackendFromString(std::string_view s) {
30 if (s == "ket" || s == "Ket" || s == "KET") {
31 return QStateBackend::Ket;
32 }
33 if (s == "dm" || s == "DM" || s == "density" || s == "rho") {
34 return QStateBackend::DM;
35 }
36 if (s == "stab" || s == "Stab" || s == "STAB" || s == "qfe" || s == "QFE") {
38 }
39
40 return QStateBackend::Ket;
41}
42
46
50
52 if (n == 0) {
53 NS_LOG_WARN("CreateState rejected: n must be greater than zero.");
54 return {};
55 }
56
57 const StateId sid = nextStateId_++;
58
59 std::shared_ptr<QState> state;
60 switch (defaultBackend_) {
62 state = std::make_shared<QStateKet>(static_cast<std::size_t>(n));
63 break;
65 state = std::make_shared<QStateDM>(static_cast<std::size_t>(n));
66 break;
68 state = std::make_shared<QStateStab>(static_cast<std::size_t>(n));
69 break;
70 }
71
72 states_[sid] = state;
73 state->SetStateId(sid);
74
75 if (state && streamAssigner_) {
76 streamAssigner_(*state);
77 }
78
79 std::vector<unsigned int> idx(n);
80 for (unsigned int i = 0; i < n; ++i) {
81 idx[i] = i;
82 }
83
85 return CreateStateResult{sid, std::move(idx)};
86}
87
88CreateStateResult QStateRegistry::CreateStateFromExisting(const std::shared_ptr<QState>& state) {
89 if (!state) {
90 NS_LOG_WARN("CreateStateFromExisting rejected: null state.");
91 return {};
92 }
93
94 const StateId newId = nextStateId_++;
95 states_.emplace(newId, state);
96 state->SetStateId(newId);
97
98 if (streamAssigner_) {
99 streamAssigner_(*state);
100 }
101
102 const auto n = static_cast<unsigned int>(state->NumQubits());
103 std::vector<unsigned int> idx(n);
104 for (unsigned int i = 0; i < n; ++i) {
105 idx[i] = i;
106 }
107
108 members_[newId] = {};
109 return {newId, std::move(idx)};
110}
111
112std::shared_ptr<QState> QStateRegistry::MergeStates(const std::vector<std::shared_ptr<Qubit>>& qs) {
113 if (qs.empty()) {
114 NS_LOG_WARN("MergeStates rejected: empty qubit list.");
115 return nullptr;
116 }
117
118 for (const auto& q : qs) {
119 if (!q) {
120 NS_LOG_WARN("MergeStates rejected: null qubit in input list.");
121 return nullptr;
122 }
123 }
124
125 std::vector<StateId> sids;
126 sids.reserve(qs.size());
127 for (const auto& q : qs) {
128 sids.push_back(q->GetStateId());
129 }
130 std::sort(sids.begin(), sids.end());
131 sids.erase(std::unique(sids.begin(), sids.end()), sids.end());
132
133 if (sids.size() == 1) {
134 return GetState(sids.front());
135 }
136
137 // Preserve each state's internal qubit order and choose a deterministic
138 // cross-state merge order so post-merge indices are reproducible.
139 struct StateBucket {
140 StateId sid;
141 std::shared_ptr<QState> state;
142 QubitId leadingQubitId;
143 std::vector<std::shared_ptr<Qubit>> members;
144 };
145
146 std::vector<StateBucket> buckets;
147 buckets.reserve(sids.size());
148
149 for (auto sid : sids) {
150 StateBucket bucket;
151 bucket.sid = sid;
152 bucket.state = GetState(sid);
153 if (!bucket.state) {
154 NS_LOG_WARN("MergeStates failed: referenced state was not found.");
155 return nullptr;
156 }
157
158 bucket.members = QubitsOf(sid);
159
160 std::sort(bucket.members.begin(), bucket.members.end(), [](const auto& x, const auto& y) {
161 return x->GetIndexInState() < y->GetIndexInState();
162 });
163
164 bucket.leadingQubitId = bucket.members.empty() ? 0 : bucket.members.front()->GetQubitId();
165
166 buckets.push_back(std::move(bucket));
167 }
168
169 std::sort(buckets.begin(), buckets.end(), [](const StateBucket& a, const StateBucket& b) {
170 if (a.leadingQubitId != b.leadingQubitId) {
171 return a.leadingQubitId < b.leadingQubitId;
172 }
173 return a.sid < b.sid;
174 });
175
176 std::vector<std::shared_ptr<Qubit>> allQs;
177 for (const auto& bucket : buckets) {
178 allQs.insert(allQs.end(), bucket.members.begin(), bucket.members.end());
179 }
180
181 auto merged = buckets.front().state;
182 for (std::size_t i = 1; i < buckets.size(); ++i) {
183 merged = merged->MergeDisjoint(*buckets[i].state);
184 if (!merged) {
185 NS_LOG_WARN("MergeStates failed: backend merge returned null.");
186 return nullptr;
187 }
188 }
189
190 auto created = CreateStateFromExisting(merged);
191 if (created.stateId == 0) {
192 return nullptr;
193 }
194
195 for (std::size_t i = 0; i < allQs.size(); ++i) {
196 const auto& q = allQs[i];
197 auto loc = GetLocation(q);
198
199 UnregisterEverywhere(q);
200 q->SetStateId(created.stateId);
201 q->SetIndexInState(created.indices[i]);
202 Register(q);
203 SetLocation(q, loc);
204 }
205
206 for (const auto& bucket : buckets) {
207 if (QubitsOf(bucket.sid).empty()) {
208 RemoveState(bucket.sid);
209 }
210 }
211
212 return GetState(created.stateId);
213}
214
215void QStateRegistry::RemoveState(StateId id) {
216 states_.erase(id);
217 members_.erase(id);
218}
219
220std::vector<std::shared_ptr<QState>> QStateRegistry::GetStatesSortedById() const {
221 std::vector<std::pair<StateId, std::shared_ptr<QState>>> items;
222 items.reserve(states_.size());
223
224 for (const auto& kv : states_) {
225 items.emplace_back(kv.first, kv.second);
226 }
227
228 std::sort(items.begin(), items.end(),
229 [](const auto& a, const auto& b) { return a.first < b.first; });
230
231 std::vector<std::shared_ptr<QState>> out;
232 out.reserve(items.size());
233 for (auto& kv : items) {
234 out.push_back(std::move(kv.second));
235 }
236
237 return out;
238}
239
240void QStateRegistry::Register(const std::shared_ptr<Qubit>& q) {
241 if (!q) {
242 NS_LOG_WARN("Register rejected: null qubit.");
243 return;
244 }
245
246 const StateId sid = q->GetStateId();
247
248 if (q->GetQubitId() == 0) {
249 q->SetQubitId(nextQubitId_++);
250 }
251
252 if (location_.count(q->GetQubitId()) == 0) {
253 SetLocation(q, MakeUnsetLocation());
254 }
255
256 auto& vec = members_[sid];
257 const auto raw = q.get();
258
259 for (const auto& sp : vec) {
260 if (sp && sp.get() == raw) {
261 return;
262 }
263 }
264
265 vec.emplace_back(q);
266}
267
268void QStateRegistry::Unregister(const std::shared_ptr<Qubit>& q) {
269 if (!q) {
270 NS_LOG_WARN("Unregister rejected: null qubit.");
271 return;
272 }
273
274 const StateId sid = q->GetStateId();
275 auto it = members_.find(sid);
276 if (it == members_.end()) {
277 return;
278 }
279
280 auto& vec = it->second;
281 const auto raw = q.get();
282
283 vec.erase(
284 std::remove_if(vec.begin(), vec.end(),
285 [&](const std::shared_ptr<Qubit>& sp) { return !sp || sp.get() == raw; }),
286 vec.end());
287}
288
289void QStateRegistry::UnregisterEverywhere(const std::shared_ptr<Qubit>& q) {
290 if (!q) {
291 NS_LOG_WARN("UnregisterEverywhere rejected: null qubit.");
292 return;
293 }
294
295 const auto raw = q.get();
296
297 for (auto& [sid, vec] : members_) {
298 (void) sid;
299 vec.erase(
300 std::remove_if(vec.begin(), vec.end(),
301 [&](const std::shared_ptr<Qubit>& sp) { return !sp || sp.get() == raw; }),
302 vec.end());
303 }
304}
305
306void QStateRegistry::SetLocation(const std::shared_ptr<Qubit>& q, Location loc) {
307 if (!q) {
308 NS_LOG_WARN("SetLocation rejected: null qubit.");
309 return;
310 }
311
312 qubitById_[q->GetQubitId()] = q;
313 SetLocation(q->GetQubitId(), loc);
314}
315
316void QStateRegistry::SetLocation(QubitId id, Location loc) {
317 auto itOld = location_.find(id);
318 if (itOld != location_.end()) {
319 const auto& oldLoc = itOld->second;
320 if (oldLoc.type == LocationType::Node) {
321 auto itSet = qubitsAtNode_.find(oldLoc.ownerId);
322 if (itSet != qubitsAtNode_.end()) {
323 itSet->second.erase(id);
324 if (itSet->second.empty()) {
325 qubitsAtNode_.erase(itSet);
326 }
327 }
328 }
329 }
330
331 location_[id] = loc;
332
333 if (loc.type == LocationType::Node) {
334 qubitsAtNode_[loc.ownerId].insert(id);
335 }
336}
337
338Location QStateRegistry::GetLocation(const std::shared_ptr<Qubit>& q) const {
339 if (!q) {
340 NS_LOG_WARN("GetLocation rejected: null qubit; returning Unset location.");
341 return MakeUnsetLocation();
342 }
343
344 auto it = location_.find(q->GetQubitId());
345 if (it == location_.end()) {
346 return MakeUnsetLocation();
347 }
348
349 return it->second;
350}
351
352Location QStateRegistry::GetLocation(QubitId id) const {
353 auto it = location_.find(id);
354 if (it == location_.end()) {
355 return MakeUnsetLocation();
356 }
357
358 return it->second;
359}
360
361std::vector<QubitId> QStateRegistry::GetQubitsAtNode(uint32_t nodeId) const {
362 std::vector<QubitId> out;
363
364 auto it = qubitsAtNode_.find(nodeId);
365 if (it == qubitsAtNode_.end()) {
366 return out;
367 }
368
369 out.reserve(it->second.size());
370 for (const auto id : it->second) {
371 out.push_back(id);
372 }
373
374 return out;
375}
376
377std::shared_ptr<Qubit> QStateRegistry::GetQubitHandle(QubitId id) const {
378 auto it = qubitById_.find(id);
379 if (it == qubitById_.end()) {
380 return nullptr;
381 }
382
383 return it->second;
384}
385
386std::vector<std::shared_ptr<Qubit>> QStateRegistry::GetLocalQubits(uint32_t nodeId) const {
387 std::vector<std::shared_ptr<Qubit>> out;
388
389 auto it = qubitsAtNode_.find(nodeId);
390 if (it == qubitsAtNode_.end()) {
391 return out;
392 }
393
394 out.reserve(it->second.size());
395 for (const auto id : it->second) {
396 if (auto q = GetQubitHandle(id)) {
397 out.push_back(q);
398 }
399 }
400
401 return out;
402}
403
404std::vector<std::shared_ptr<Qubit>> QStateRegistry::QubitsOf(StateId stateId) const {
405 std::vector<std::shared_ptr<Qubit>> out;
406
407 auto it = members_.find(stateId);
408 if (it == members_.end()) {
409 return out;
410 }
411
412 out.reserve(it->second.size());
413 for (const auto& sp : it->second) {
414 if (sp) {
415 out.push_back(sp);
416 }
417 }
418
419 return out;
420}
421
422std::shared_ptr<QState> QStateRegistry::GetState(const std::shared_ptr<Qubit>& q) const {
423 if (!q) {
424 return nullptr;
425 }
426
427 return GetState(q->GetStateId());
428}
429
430std::shared_ptr<QState> QStateRegistry::GetState(StateId stateId) const {
431 auto it = states_.find(stateId);
432 if (it == states_.end()) {
433 NS_LOG_WARN("GetState failed: state id " << stateId << " was not found.");
434 return nullptr;
435 }
436
437 return it->second;
438}
439
440} // namespace q2ns
QStateBackend GetDefaultBackend() const
Get the default backend used for newly created states.
std::unordered_map< StateId, std::vector< std::shared_ptr< Qubit > > > members_
State id to shared member qubit handles.
StreamAssigner streamAssigner_
Callback used when a new state is registered.
QStateBackend defaultBackend_
Default backend for CreateState().
void SetDefaultBackend(QStateBackend b)
Set the default backend used for newly created states.
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.
CreateStateResult CreateStateFromExisting(const std::shared_ptr< QState > &state)
Register an already-constructed backend state object.
StateId nextStateId_
Next state id to assign.
std::unordered_map< StateId, std::shared_ptr< QState > > states_
State id to backend state.
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
std::uint64_t StateId
Stable identifier for a registered backend state.
Definition q2ns-types.h:55
Location MakeUnsetLocation()
Construct an Unset location value.
Definition q2ns-types.h:156
QStateBackend BackendFromString(std::string_view s)
Convert a backend name string to a QStateBackend enum value.
QStateBackend
Backend family used when creating new quantum states.
Definition q2ns-types.h:94
@ DM
Density-matrix backend.
@ Stab
Stabilizer backend.
@ Ket
State-vector backend.
uint64_t sid
Result of creating a new backend state.
Current tracked location of a qubit.
Definition q2ns-types.h:136
LocationType type
Kind of location.
Definition q2ns-types.h:140
uint32_t ownerId
Owning object identifier.
Definition q2ns-types.h:148