14#include "ns3/q2ns-qstate-dm.h"
16#include "ns3/assert.h"
26 constexpr double dimTol = 1e-9;
27 constexpr double hermTol = 1e-12;
28 constexpr double traceTol = 1e-9;
29 constexpr double psdTol = 1e-12;
31 const auto rows = rho.rows();
32 const auto cols = rho.cols();
35 throw std::invalid_argument(
"QStateDM: density matrix must be square");
39 throw std::invalid_argument(
"QStateDM: density matrix must be non-empty");
42 const double log2dim = std::log2(
static_cast<double>(rows));
43 if (std::fabs(std::round(log2dim) - log2dim) > dimTol) {
44 throw std::invalid_argument(
"QStateDM: dim(rho) must be 2^N for some integer N");
48 if (!rho.isApprox(rho.adjoint(), hermTol)) {
49 throw std::invalid_argument(
"QStateDM: density matrix must be Hermitian");
53 const std::complex<double> tr = qpp::trace(rho);
54 if (std::fabs(std::imag(tr)) > traceTol) {
55 throw std::invalid_argument(
"QStateDM: Tr(rho) must be real");
57 if (std::fabs(std::real(tr) - 1.0) > traceTol) {
58 throw std::invalid_argument(
"QStateDM: Tr(rho) must be 1");
63 const qpp::cmat
H = 0.5 * (rho + rho.adjoint());
65 Eigen::SelfAdjointEigenSolver<qpp::cmat> es(
H);
66 if (es.info() != Eigen::Success) {
67 throw std::runtime_error(
"QStateDM: eigensolver failed while validating density matrix");
70 const auto& evals = es.eigenvalues();
71 for (
int i = 0; i < evals.size(); ++i) {
72 if (evals[i] < -psdTol) {
73 throw std::invalid_argument(
"QStateDM: density matrix must be positive semidefinite");
81 std::vector<qpp::idx> zeros(numQubits, 0);
82 qpp::ket k = qpp::mket(zeros);
96 constexpr uint64_t QPPD_SALT = 0x51505044ULL;
100 qpp::RandomDevices::get_instance().get_prng().seed(
seq);
107 const auto dim =
static_cast<double>(
rho_.rows());
108 return static_cast<std::size_t
>(std::llround(std::log2(
dim)));
121 std::vector<qpp::idx>
t;
124 t.push_back(
static_cast<qpp::idx
>(
i));
134 const std::size_t
k =
targets.size();
135 const std::size_t
dim = 1ull <<
k;
137 "QStateDM::Apply: wrong gate dimension");
147 throw std::runtime_error(
"QStateDM::MergeDisjoint: incompatible backend");
151 return std::make_shared<QStateDM>(
combined);
159 throw std::out_of_range(
"QStateDM::Measure: target out of range");
169 rho_ = qpp::apply(
rho_, qpp::adjoint(qpp::gt.
S), {
static_cast<qpp::idx
>(
target)});
174 std::vector<qpp::idx>
dims(
n, 2);
175 std::vector<qpp::idx>
subsys{
static_cast<qpp::idx
>(
target)};
178 const qpp::idx outcome = std::get<0>(
meas_res);
183 qpp::ket
oneQ = (outcome == 0 ? qpp::mket({0}) : qpp::mket({1}));
191 oneQ = qpp::apply(
oneQ, qpp::adjoint(qpp::gt.
S), {0});
197 auto survivors = std::make_shared<QStateDM>(std::move(
states[outcome]));
207 throw std::runtime_error(
"QStateDM::PartialTrace: empty state");
210 throw std::invalid_argument(
211 "QStateDM::PartialTrace: subsystemA must be a non-empty proper subset");
214 std::vector<bool>
used(
n,
false);
215 std::vector<qpp::idx>
A;
220 throw std::out_of_range(
"QStateDM::PartialTrace: index out of range");
223 throw std::invalid_argument(
"QStateDM::PartialTrace: duplicate index");
226 A.push_back(
static_cast<qpp::idx
>(
idxUser));
229 std::vector<qpp::idx>
B;
230 B.reserve(
n -
A.size());
237 std::vector<qpp::idx>
dims(
n, 2);
247 return std::make_shared<QStateDM>(
rhoA);
Lightweight gate descriptor used by QState backends.
Density-matrix concrete QState backend using qpp::cmat.
MeasureResult Measure(q2ns::Index target, q2ns::Basis basis=q2ns::Basis::Z) override
Measure one qubit in the requested basis and split the result.
QStateDM(std::size_t numQubits)
Construct the |0...0><0...0| state on numQubits qubits.
static void ValidateDensityMatrix(const qpp::cmat &rho)
Validate basic density-matrix structure.
std::size_t NumQubits() const override
Return the number of logical qubits in the state.
std::shared_ptr< QState > PartialTrace(const std::vector< q2ns::Index > &subsystemA)
Extract a subsystem by partial trace.
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.
void Apply(const QGate &g, const std::vector< q2ns::Index > &targets) override
Apply a gate to the given target qubits.
qpp::cmat rho_
Backend density matrix.
std::shared_ptr< QState > MergeDisjoint(const QState &other) const override
Return the disjoint merge of this state and another density-matrix backend.
void SetRho(const qpp::cmat &rho)
Replace the underlying density matrix after validation.
Backend-agnostic interface for a quantum state object.
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.
std::size_t Index
Generic qubit index type within a backend state.
@ Custom
Caller-supplied matrix.
const Matrix & MatrixOf(QGateKind k)
Return the built-in matrix for a non-custom gate kind.
Result of measuring one qubit and splitting the state.