Q2NS dev
ns-3 module
Loading...
Searching...
No Matches
q2ns-qstate-ket.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-ket.cc
11 * @brief Defines q2ns::QStateKet.
12 */
13
14#include "ns3/q2ns-qstate-ket.h"
15
16#include "ns3/assert.h"
17
18#include <cmath>
19#include <cstdint>
20#include <stdexcept>
21#include <utility>
22#include <vector>
23
24namespace q2ns {
25
26void QStateKet::ValidateKet(const qpp::ket& k) {
27 constexpr double dimTol = 1e-9;
28 constexpr double normTol = 1e-9;
29
30 if (k.rows() <= 0) {
31 throw std::invalid_argument("QStateKet: ket must be non-empty");
32 }
33
34 if (k.cols() != 1) {
35 throw std::invalid_argument("QStateKet: ket must be a column vector");
36 }
37
38 const double log2dim = std::log2(static_cast<double>(k.rows()));
39 if (std::fabs(std::round(log2dim) - log2dim) > dimTol) {
40 throw std::invalid_argument("QStateKet: dim(ket) must be 2^N for some integer N");
41 }
42
43 const double norm2 = std::real((k.adjoint() * k)(0, 0));
44 if (std::fabs(norm2 - 1.0) > normTol) {
45 throw std::invalid_argument("QStateKet: ket must be normalized");
46 }
47}
48
49
50
51QStateKet::QStateKet(std::size_t numQubits) {
52 std::vector<qpp::idx> zeros(numQubits, 0);
53 state_ = qpp::mket(zeros);
54}
55
56
57
58QStateKet::QStateKet(qpp::ket state) : state_(std::move(state)) {
60}
61
62
63
64int64_t QStateKet::AssignStreams(int64_t stream) {
65 constexpr uint64_t QPPK_SALT = 0x5150504BULL; // "QPPK"
66
69 qpp::RandomDevices::get_instance().get_prng().seed(seq);
70 });
71}
72
73
74
75std::size_t QStateKet::NumQubits() const {
76 return static_cast<std::size_t>(std::llround(std::log2(state_.rows())));
77}
78
79
80
81void QStateKet::Print(std::ostream& os) const {
82 PrintHeader(os, "Ket");
83 os << state_ << "\n";
84}
85
86
87
88void QStateKet::Apply(const QGate& g, const std::vector<q2ns::Index>& targets) {
89 std::vector<qpp::idx> t;
90 t.reserve(targets.size());
91 for (auto i : targets) {
92 t.push_back(static_cast<qpp::idx>(i));
93 }
94
95 qpp::cmat U;
96 if (g.Kind() == QGateKind::Custom) {
97 U = g.Unitary();
98 } else {
99 U = MatrixOf(g.Kind());
100 }
101
102 const std::size_t k = targets.size();
103 const std::size_t dim = 1ull << k;
104 NS_ABORT_MSG_IF(U.rows() != static_cast<int>(dim) || U.cols() != static_cast<int>(dim),
105 "QStateKet::Apply: wrong gate dimension");
106
107 state_ = qpp::apply(state_, U, t);
108}
109
110
111
112std::shared_ptr<QState> QStateKet::MergeDisjoint(const QState& other) const {
113 const auto* otherKet = dynamic_cast<const QStateKet*>(&other);
114 if (!otherKet) {
115 throw std::runtime_error("QStateKet::MergeDisjoint: incompatible backend");
116 }
117
118 const qpp::ket combined = qpp::kron(state_, otherKet->state_);
119 return std::make_shared<QStateKet>(combined);
120}
121
122
123
125 if (target >= NumQubits()) {
126 throw std::out_of_range("QStateKet::Measure: target index out of range");
127 }
128
129 switch (basis) {
130 case Basis::Z:
131 break;
132 case Basis::X:
133 state_ = qpp::apply(state_, qpp::gt.H, {static_cast<qpp::idx>(target)});
134 break;
135 case Basis::Y:
136 state_ = qpp::apply(state_, qpp::adjoint(qpp::gt.S), {static_cast<qpp::idx>(target)});
137 state_ = qpp::apply(state_, qpp::gt.H, {static_cast<qpp::idx>(target)});
138 break;
139 }
140
141 auto [res, probs, states] = qpp::measure(state_, qpp::gt.Z, {static_cast<qpp::idx>(target)});
142 (void) probs;
143
144 // Construct the measured 1-qubit state in the requested basis rather than
145 // leaving it in the intermediate rotated Z-basis representation.
146 qpp::ket oneQubit = (res == 0 ? qpp::mket({0}) : qpp::mket({1}));
147 switch (basis) {
148 case Basis::Z:
149 break;
150 case Basis::X:
151 oneQubit = qpp::apply(oneQubit, qpp::gt.H, {0});
152 break;
153 case Basis::Y:
154 oneQubit = qpp::apply(oneQubit, qpp::adjoint(qpp::gt.S), {0});
155 oneQubit = qpp::apply(oneQubit, qpp::gt.H, {0});
156 break;
157 }
158
159 auto measured1q = std::make_shared<QStateKet>(std::move(oneQubit));
160 auto survivors = std::make_shared<QStateKet>(std::move(states[res]));
161 return MeasureResult{static_cast<int>(res), measured1q, survivors};
162}
163
164
165
166const qpp::ket& QStateKet::GetKet() const {
167 return state_;
168}
169
170
171
172void QStateKet::SetKet(const qpp::ket& k) {
173 ValidateKet(k);
174 state_ = k;
175}
176
177
178
180 return qpp::prj(state_);
181}
182
183} // namespace q2ns
Lightweight gate descriptor used by QState backends.
Definition q2ns-qgate.h:68
Ket-based concrete QState backend using qpp.
int64_t AssignStreams(int64_t stream) override
Assign RNG streams for deterministic randomness.
void Print(std::ostream &os) const override
Print a human-readable representation of the state.
QStateKet(std::size_t numQubits)
Construct the |0...0> state on numQubits qubits.
qpp::cmat GetDensityMatrix() const
Return the density matrix |psi><psi| of the current ket.
qpp::ket state_
Pure state ket.
static void ValidateKet(const qpp::ket &k)
Validate basic ket properties.
MeasureResult Measure(q2ns::Index target, q2ns::Basis basis=q2ns::Basis::Z) override
Measure one qubit in the requested basis and split the result.
const qpp::ket & GetKet() const
Return the underlying ket.
std::size_t NumQubits() const override
Return the number of logical qubits in the state.
std::shared_ptr< QState > MergeDisjoint(const QState &other) const override
Return the disjoint merge of this state and another ket backend.
void SetKet(const qpp::ket &k)
Replace the underlying ket.
void Apply(const QGate &g, const std::vector< q2ns::Index > &targets) override
Apply a gate to the given target qubits.
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 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.
const Matrix & MatrixOf(QGateKind k)
Return the built-in matrix for a non-custom gate kind.
Definition q2ns-qgate.h:313
Result of measuring one qubit and splitting the state.