Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qstate-stab.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-stab.cc
11 * @brief Defines q2ns::QStateStab.
12 */
13
14#include "ns3/q2ns-qstate-stab.h"
15
16#include "random.h"
17
18#include <cstdint>
19#include <stdexcept>
20
21namespace q2ns {
22
23QStateStab::QStateStab(std::size_t numQubits)
24 : numQubits_(numQubits), psi_(static_cast<int>(numQubits)) {}
25
26
27
28int64_t QStateStab::AssignStreams(int64_t stream) {
29 constexpr uint64_t STAB_SALT = 0x53544142ULL; // "STAB"
30
31 return QState::AssignStreamsGlobal<STAB_SALT>(stream, [&](uint64_t s64) { stab::Seed(s64); });
32}
33
34
35
36std::size_t QStateStab::NumQubits() const {
37 return numQubits_;
38}
39
40
41
42void QStateStab::Print(std::ostream& os) const {
43 PrintHeader(os, "Stab");
44 os << psi_ << "\n";
45}
46
47
48
49void QStateStab::Apply(const QGate& g, const std::vector<Index>& t) {
50 const auto arity = t.size();
51
52 if (g.Kind() == QGateKind::Custom) {
53 throw std::runtime_error("QStateStab does not support custom gates");
54 }
55
56 if (arity == 1) {
57 const int q = static_cast<int>(t[0]);
58 switch (g.Kind()) {
59 case QGateKind::I:
60 return;
61 case QGateKind::H:
62 psi_.H(q);
63 return;
64 case QGateKind::S:
65 psi_.S(q);
66 return;
67 case QGateKind::SDG:
68 psi_.SDG(q);
69 return;
70 case QGateKind::X:
71 psi_.X(q);
72 return;
73 case QGateKind::Y:
74 psi_.Y(q);
75 return;
76 case QGateKind::Z:
77 psi_.Z(q);
78 return;
79 default:
80 break;
81 }
82 throw std::runtime_error("QStateStab supports only 1-qubit Clifford gates");
83 }
84
85 if (arity == 2) {
86 const int a = static_cast<int>(t[0]);
87 const int b = static_cast<int>(t[1]);
88 switch (g.Kind()) {
89 case QGateKind::CNOT:
90 psi_.CX(a, b);
91 return;
92 case QGateKind::CZ:
93 psi_.CZ(a, b);
94 return;
95 case QGateKind::SWAP:
96 psi_.SWAP(a, b);
97 return;
98 default:
99 break;
100 }
101 throw std::runtime_error("QStateStab supports only 2-qubit Clifford gates");
102 }
103
104 throw std::runtime_error("QStateStab supports only 1-qubit and 2-qubit gates");
105}
106
108 const int j = static_cast<int>(q);
109
110 if (basis == Basis::Z) {
111 return;
112 }
113 if (basis == Basis::X) {
114 psi_.H(j);
115 return;
116 }
117 if (basis == Basis::Y) {
118 psi_.SDG(j);
119 psi_.H(j);
120 return;
121 }
122
123 throw std::runtime_error("QStateStab: unsupported measurement basis");
124}
125
126std::shared_ptr<QStateStab> QStateStab::Synth1QEigenstate_(Basis basis, int bit) {
127 auto st = std::make_shared<QStateStab>(1);
128
129 // Start in |0>, prepare the +1 eigenstate for the requested basis, then
130 // apply Z when bit=1 to obtain the -1 eigenstate.
131 if (basis == Basis::Z) {
132 if (bit) {
133 st->psi_.X(0);
134 }
135 return st;
136 }
137 if (basis == Basis::X) {
138 st->psi_.H(0);
139 if (bit) {
140 st->psi_.Z(0);
141 }
142 return st;
143 }
144 if (basis == Basis::Y) {
145 st->psi_.H(0);
146 st->psi_.S(0);
147 if (bit) {
148 st->psi_.Z(0);
149 }
150 return st;
151 }
152
153 throw std::runtime_error("QStateStab: unsupported basis in Synth1QEigenstate_");
154}
155
156
157
159 if (numQubits_ == 0) {
160 throw std::runtime_error("QStateStab::RemoveQubit_: empty state");
161 }
162 if (target >= numQubits_) {
163 throw std::runtime_error("QStateStab::RemoveQubit_: target out of range");
164 }
165
166 // Swap the target to the end so dropping the last qubit preserves the
167 // original relative order of survivors.
168 for (Index q = target; q + 1 < numQubits_; ++q) {
169 psi_.SWAP(static_cast<int>(q), static_cast<int>(q + 1));
170 }
171
172 psi_.DropLastQubit();
173 --numQubits_;
174}
175
176
177
179 if (target >= numQubits_) {
180 throw std::runtime_error("QStateStab::Measure: target out of range");
181 }
182
183 // Standard pattern:
184 // 1) rotate so Z measurement implements the requested basis
185 // 2) measure Z
186 // 3) build the measured 1-qubit eigenstate and the survivor state
188
189 const int bit = psi_.MeasureZ(static_cast<int>(target));
190
191 auto survivors = std::make_shared<QStateStab>(*this);
192 survivors->RemoveQubit_(target);
193
194 auto measured = Synth1QEigenstate_(basis, bit);
195
197 r.outcome = bit;
198 r.measured = std::move(measured);
199 r.survivors = std::move(survivors);
200 return r;
201}
202
203
204
205std::shared_ptr<QState> QStateStab::MergeDisjoint(const QState& other) const {
206 const auto* rhs = dynamic_cast<const QStateStab*>(&other);
207 if (!rhs) {
208 throw std::runtime_error("QStateStab::MergeDisjoint: incompatible backend");
209 }
210
211 stab::AffineState mergedPsi = stab::AffineState::TensorProduct(this->psi_, rhs->psi_);
212
213 auto out = std::make_shared<QStateStab>(0);
214 out->numQubits_ = this->numQubits_ + rhs->numQubits_;
215 out->psi_ = std::move(mergedPsi);
216 return out;
217}
218
219} // namespace q2ns
Lightweight gate descriptor used by QState backends.
Definition q2ns-qgate.h:68
Stabilizer concrete QState backend using stab::AffineState.
MeasureResult Measure(Index target, Basis basis=Basis::Z) override
Measure one qubit in the requested basis and split the result.
void RemoveQubit_(Index target)
Remove one qubit while preserving survivor order.
std::size_t numQubits_
Number of logical qubits.
QStateStab(std::size_t numQubits)
Construct the |0...0> stabilizer state on numQubits qubits.
void RotateIntoZBasis_(Index q, Basis basis)
Rotate a local basis so that measuring Z implements the requested basis.
std::shared_ptr< QState > MergeDisjoint(const QState &other) const override
Return the disjoint merge of this state and another stabilizer backend.
std::size_t NumQubits() const override
Return the number of logical qubits in the state.
static std::shared_ptr< QStateStab > Synth1QEigenstate_(Basis basis, int bit)
Build a 1-qubit basis eigenstate for a measurement outcome.
stab::AffineState psi_
Underlying stabilizer state.
void Print(std::ostream &os) const override
Print a human-readable representation of the state.
void Apply(const QGate &g, const std::vector< Index > &t) override
Apply a supported Clifford gate to the given target qubits.
int64_t AssignStreams(int64_t stream) override
Assign RNG streams for deterministic randomness.
Backend-agnostic interface for a quantum state object.
Definition q2ns-qstate.h:51
void PrintHeader(std::ostream &os, const char *backendName) const
Print a standard backend header.
static int64_t AssignStreamsGlobal(int64_t stream, ReseedFn reseed_fn)
Helper for backends that reseed a global RNG source.
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
@ Custom
Caller-supplied matrix.
Result of measuring one qubit and splitting the state.
int outcome
Classical outcome bit.