Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qstate.h
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.h
11 * @brief Declares q2ns::QState, the backend-agnostic quantum state interface.
12 */
13
14#pragma once
15
16#include "ns3/q2ns-qgate.h"
17#include "ns3/q2ns-types.h"
18
19#include "ns3/abort.h"
20#include "ns3/rng-seed-manager.h"
21
22#include <cstdint>
23#include <iosfwd>
24#include <memory>
25#include <mutex>
26#include <random>
27#include <vector>
28
29namespace q2ns {
30
31/**
32 * @ingroup q2ns_qstate
33 * @class QState
34 * @brief Backend-agnostic interface for a quantum state object.
35 *
36 * QState defines the minimal common interface implemented by concrete backend
37 * state classes such as ket, density-matrix, and stabilizer backends.
38 *
39 * Design goals:
40 * - Keep high-level code backend-agnostic.
41 * - Define deterministic index-order semantics for merge and measurement.
42 * - Centralize backend stream-assignment helpers shared across implementations.
43 *
44 * Concrete implementations may expose backend-specific views on their own
45 * concrete classes, but QState provides the common operational interface used
46 * by QProcessor and QStateRegistry.
47 *
48 * @see QProcessor
49 * @see QStateRegistry
50 */
51class QState {
52public:
53 /**
54 * @brief Virtual destructor.
55 */
56 virtual ~QState();
57
58 /**
59 * @brief Assign RNG streams for deterministic randomness.
60 *
61 * Backends that use random sampling should deterministically seed or bind
62 * their random sources here using the provided stream index together with the
63 * current ns-3 seed and run values.
64 *
65 * Backends that consume no random streams may keep the default behavior.
66 *
67 * @param stream Starting stream index.
68 * @return Number of streams consumed.
69 */
71 (void) stream;
72 return 0;
73 }
74
75 /**
76 * @brief Get the registry-assigned state id.
77 * @return Current state id.
78 */
80 return stateId_;
81 }
82
83 /**
84 * @brief Set the registry-assigned state id.
85 * @param id New state id.
86 */
88 stateId_ = id;
89 }
90
91 /**
92 * @brief Print a human-readable representation of the state.
93 * @param os Output stream.
94 */
95 virtual void Print(std::ostream& os) const = 0;
96
97 /**
98 * @brief Return the number of logical qubits in the state.
99 * @return Number of logical qubits.
100 */
101 virtual std::size_t NumQubits() const = 0;
102
103 /**
104 * @brief Apply a gate to the given target indices.
105 *
106 * Concrete backends may support different gate subsets. For example,
107 * matrix-based backends may accept arbitrary custom unitaries while other
108 * backends may reject non-native operations.
109 *
110 * @param g Gate descriptor.
111 * @param targets Target qubit indices.
112 */
113 virtual void Apply(const QGate& g, const std::vector<Index>& targets) = 0;
114
115 /**
116 * @brief Return a new state that is the disjoint merge of this state and another.
117 *
118 * Index-order invariant:
119 * - The result first contains this state's qubits in their current index
120 * order.
121 * - It then contains the other state's qubits in their current index order.
122 *
123 * @param other Other state to merge with.
124 * @return Newly allocated merged state object.
125 */
126 virtual std::shared_ptr<QState> MergeDisjoint(const QState& other) const = 0;
127
128 /**
129 * @struct MeasureResult
130 * @brief Result of measuring one qubit and splitting the state.
131 *
132 * The measured qubit is returned as a new 1-qubit state in measured. The
133 * remaining qubits are returned as a separate survivors state.
134 */
136 int outcome{0}; //!< Classical outcome bit.
137 std::shared_ptr<QState> measured; //!< Measured 1-qubit state.
138 std::shared_ptr<QState> survivors; //!< Remaining qubits after measurement.
139 };
140
141 /**
142 * @brief Measure one qubit and split the result into new states.
143 *
144 * Survivors preserve their original relative order. Any original indices
145 * greater than the measured target shift down by one in the survivors state.
146 *
147 * @param target Index of the qubit to measure.
148 * @param basis Measurement basis. Defaults to Z.
149 * @return Measurement result containing outcome, measured state, and survivor state.
150 */
152
153protected:
154 /**
155 * @brief Print a standard backend header.
156 * @param os Output stream.
157 * @param backendName Human-readable backend name.
158 */
159 void PrintHeader(std::ostream& os, const char* backendName) const {
160 os << "QState{backend=" << backendName << ", id=" << GetStateId() << ", n=" << NumQubits()
161 << "}\n";
162 }
163
164 /**
165 * @brief Mix a 64-bit value deterministically.
166 * @param x Input value.
167 * @return Mixed value.
168 */
169 static inline uint64_t SplitMix64(uint64_t x) {
170 x += 0x9e3779b97f4a7c15ULL;
171 x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
172 x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
173 return x ^ (x >> 31);
174 }
175
176 /**
177 * @brief Derive a deterministic 64-bit seed from ns-3 seed/run and a stream.
178 * @param seed ns-3 global seed.
179 * @param run ns-3 global run.
180 * @param stream Stream index.
181 * @param salt Backend-specific salt.
182 * @return Derived 64-bit seed.
183 */
185 uint64_t x = (uint64_t(seed) << 32) ^ uint64_t(run) ^ uint64_t(stream) ^ salt;
186 return SplitMix64(x);
187 }
188
189 /**
190 * @brief Build a std::seed_seq from a 64-bit seed.
191 * @param s64 Derived 64-bit seed.
192 * @return Seed sequence suitable for standard engines.
193 */
194 static inline std::seed_seq MakeSeedSeq(uint64_t s64) {
195 const uint32_t a = static_cast<uint32_t>(s64);
196 const uint32_t b = static_cast<uint32_t>(s64 >> 32);
197 return std::seed_seq{a, b, 0x13579bdu, 0x2468ace0u};
198 }
199
200 /**
201 * @brief Abort if ns-3 seed or run is zero.
202 * @param seed ns-3 global seed.
203 * @param run ns-3 global run.
204 */
205 static inline void CheckSeedRunNonZero(uint32_t seed, uint32_t run) {
206 NS_ABORT_MSG_IF(seed == 0, "RngSeedManager::GetSeed() returned 0; Seed must be > 0");
207 NS_ABORT_MSG_IF(run == 0, "RngSeedManager::GetRun() returned 0; Run must be > 0");
208 }
209
210 /**
211 * @brief Return whether a backend-global RNG should be reseeded.
212 *
213 * This caches the most recent seed, run, and stream tuple for a given SALT.
214 *
215 * @tparam SALT Backend-specific salt.
216 * @param seed ns-3 global seed.
217 * @param run ns-3 global run.
218 * @param stream Stream index.
219 * @return True if reseeding should occur.
220 */
221 template <uint64_t SALT> static bool ShouldReseed(uint32_t seed, uint32_t run, int64_t stream) {
222 struct Key {
223 uint32_t seed;
226 };
227
228 static std::mutex m;
229 static bool hasLast = false;
230 static Key last{};
231
232 std::lock_guard<std::mutex> lock(m);
233 if (hasLast && last.seed == seed && last.run == run && last.stream == stream) {
234 return false;
235 }
236 last = Key{seed, run, stream};
237 hasLast = true;
238 return true;
239 }
240
241 /**
242 * @brief Helper for backends that reseed a global RNG source.
243 *
244 * This helper reads ns-3 seed and run values, checks they are valid, skips
245 * redundant reseeding when the configuration is unchanged, derives a 64-bit
246 * seed, and invokes the provided reseed callback.
247 *
248 * @tparam SALT Backend-specific salt.
249 * @tparam ReseedFn Callback type used to perform backend reseeding.
250 * @param stream Stream index.
251 * @param reseed_fn Callback that accepts the derived 64-bit seed.
252 * @return Number of streams conceptually consumed.
253 */
254 template <uint64_t SALT, typename ReseedFn>
256 const uint32_t seed = ns3::RngSeedManager::GetSeed();
257 const uint32_t run = ns3::RngSeedManager::GetRun();
259
260 if (!ShouldReseed<SALT>(seed, run, stream)) {
261 return 1;
262 }
263
264 const uint64_t s64 = DeriveSeed64(seed, run, stream, SALT);
265 reseed_fn(s64);
266 return 1;
267 }
268
269private:
270 StateId stateId_{0}; //!< Registry-assigned state id.
271};
272
273/**
274 * @brief Stream insertion for a QState reference.
275 * @param os Output stream.
276 * @param s State reference.
277 * @return Output stream.
278 */
279inline std::ostream& operator<<(std::ostream& os, const QState& s) {
280 s.Print(os);
281 return os;
282}
283
284/**
285 * @brief Stream insertion for a shared QState pointer.
286 * @param os Output stream.
287 * @param s Shared state pointer.
288 * @return Output stream.
289 */
290inline std::ostream& operator<<(std::ostream& os, const std::shared_ptr<QState>& s) {
291 if (s) {
292 s->Print(os);
293 } else {
294 os << "QState{null}";
295 }
296 return os;
297}
298
299} // namespace q2ns
Lightweight gate descriptor used by QState backends.
Definition q2ns-qgate.h:68
Backend-agnostic interface for a quantum state object.
Definition q2ns-qstate.h:51
static std::seed_seq MakeSeedSeq(uint64_t s64)
Build a std::seed_seq from a 64-bit seed.
void SetStateId(StateId id)
Set the registry-assigned state id.
Definition q2ns-qstate.h:87
virtual std::size_t NumQubits() const =0
Return the number of logical qubits in the state.
virtual int64_t AssignStreams(int64_t stream)
Assign RNG streams for deterministic randomness.
Definition q2ns-qstate.h:70
virtual void Print(std::ostream &os) const =0
Print a human-readable representation of the state.
static uint64_t SplitMix64(uint64_t x)
Mix a 64-bit value deterministically.
virtual std::shared_ptr< QState > MergeDisjoint(const QState &other) const =0
Return a new state that is the disjoint merge of this state and another.
void PrintHeader(std::ostream &os, const char *backendName) const
Print a standard backend header.
static uint64_t DeriveSeed64(uint32_t seed, uint32_t run, int64_t stream, uint64_t salt)
Derive a deterministic 64-bit seed from ns-3 seed/run and a stream.
static void CheckSeedRunNonZero(uint32_t seed, uint32_t run)
Abort if ns-3 seed or run is zero.
StateId stateId_
Registry-assigned state id.
static int64_t AssignStreamsGlobal(int64_t stream, ReseedFn reseed_fn)
Helper for backends that reseed a global RNG source.
static bool ShouldReseed(uint32_t seed, uint32_t run, int64_t stream)
Return whether a backend-global RNG should be reseeded.
virtual MeasureResult Measure(q2ns::Index target, q2ns::Basis basis=q2ns::Basis::Z)=0
Measure one qubit and split the result into new states.
virtual void Apply(const QGate &g, const std::vector< Index > &targets)=0
Apply a gate to the given target indices.
virtual ~QState()
Virtual destructor.
StateId GetStateId() const
Get the registry-assigned state id.
Definition q2ns-qstate.h:79
std::uint64_t StateId
Stable identifier for a registered backend state.
Definition q2ns-types.h:55
Basis
Measurement basis for single-qubit projective measurement.
Definition q2ns-types.h:88
std::size_t Index
Generic qubit index type within a backend state.
Definition q2ns-types.h:49
std::ostream & operator<<(std::ostream &os, const QState &s)
Stream insertion for a QState reference.
Result of measuring one qubit and splitting the state.
std::shared_ptr< QState > measured
Measured 1-qubit state.
int outcome
Classical outcome bit.
std::shared_ptr< QState > survivors
Remaining qubits after measurement.