14#include "ns3/q2ns-analysis.h"
16#include "ns3/q2ns-qstate-all.h"
17#include "ns3/q2ns-types.h"
48 if (
const auto* k =
dynamic_cast<const QStateKet*
>(&s)) {
49 return k->GetDensityMatrix();
52 if (
const auto* d =
dynamic_cast<const QStateDM*
>(&s)) {
56 if (
const auto* st =
dynamic_cast<const QStateStab*
>(&s)) {
58 return qpp::prj(st->GetAffineState().to_ket());
60 throw std::runtime_error(
"ToDensityMatrix(QStateStab): unavailable without USE_QPP support");
64 throw std::runtime_error(
"ToDensityMatrix: unsupported QState backend");
83 if (x < lo && x > lo - tol) {
86 if (x > hi && x < hi + tol) {
104 if (A.rows() != A.cols()) {
105 throw std::runtime_error(
"HermitianSqrtPSD: matrix must be square");
109 Eigen::MatrixXcd
H = 0.5 * (A + A.adjoint());
111 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> es(
H);
112 if (es.info() != Eigen::Success) {
113 throw std::runtime_error(
"HermitianSqrtPSD: eigensolver failed");
116 Eigen::VectorXd evals = es.eigenvalues();
117 Eigen::MatrixXcd evecs = es.eigenvectors();
119 for (
int i = 0; i < evals.size(); ++i) {
120 if (evals[i] < 0.0) {
121 if (evals[i] > -negEigTol) {
124 throw std::runtime_error(
"HermitianSqrtPSD: matrix is not positive semidefinite");
129 Eigen::VectorXd sqrtEvals = evals.array().sqrt();
130 return evecs * sqrtEvals.asDiagonal() * evecs.adjoint();
146 double negEigTol = 1e-12) {
147 if (rho.rows() != rho.cols() || sigma.rows() != sigma.cols() || rho.rows() != sigma.rows()) {
148 throw std::runtime_error(
"UhlmannFidelity: dimension mismatch");
152 Eigen::MatrixXcd
X = sqrtRho * sigma * sqrtRho;
155 std::complex<double> tr = sqrtX.trace();
157 double f = std::real(tr);
161 if (f < 0.0 && f > -1e-10) {
164 if (f > 1.0 && f < 1.0 + 1e-10) {
189 if (A.rows() != A.cols()) {
190 throw std::runtime_error(
"HermitianTraceNorm: matrix must be square");
193 Eigen::MatrixXcd
H = 0.5 * (A + A.adjoint());
195 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> es(
H);
196 if (es.info() != Eigen::Success) {
197 throw std::runtime_error(
"HermitianTraceNorm: eigensolver failed");
200 return es.eigenvalues().cwiseAbs().sum();
209 throw std::runtime_error(
"Fidelity: states must have the same number of qubits");
214 throw std::invalid_argument(
"Fidelity: empty states are not supported");
218 if (
auto ak =
dynamic_cast<const QStateKet*
>(&a)) {
219 if (
auto bk =
dynamic_cast<const QStateKet*
>(&b)) {
220 const auto& psi = ak->GetKet();
221 const auto& phi = bk->GetKet();
223 const auto amp = (psi.adjoint() * phi)(0, 0);
224 double f = std::norm(amp);
225 return ClampToInterval(f, 0.0, 1.0);
230 if (
auto ad =
dynamic_cast<const QStateDM*
>(&a)) {
231 if (
auto bd =
dynamic_cast<const QStateDM*
>(&b)) {
232 const auto& rho = ad->GetRho();
233 const auto& sigma = bd->GetRho();
234 return UhlmannFidelity(rho, sigma);
240 if (
auto as =
dynamic_cast<const QStateStab*
>(&a)) {
241 if (
auto bs =
dynamic_cast<const QStateStab*
>(&b)) {
243 const auto& psiS = as->GetAffineState();
244 const auto& phiS = bs->GetAffineState();
246 const auto psi = psiS.to_ket();
247 const auto phi = phiS.to_ket();
249 const auto amp = (psi.adjoint() * phi)(0, 0);
250 double f = std::norm(amp);
251 return ClampToInterval(f, 0.0, 1.0);
253 throw std::runtime_error(
"Fidelity(Stab, Stab) is unavailable without USE_QPP support");
258 throw std::runtime_error(
"Fidelity: mixed backends are not supported. "
259 "Supported pairs are Ket-Ket, DM-DM, and Stab-Stab.");
267 throw std::invalid_argument(
"Purity: empty states are not supported");
270 const auto rho = ToDensityMatrix(s);
271 const std::complex<double> tr = qpp::trace(rho * rho);
273 double p = std::real(tr);
274 p = ClampToInterval(p, 0.0, 1.0);
282 throw std::invalid_argument(
"IsPure: tolerance must be non-negative");
285 return std::fabs(
Purity(s) - 1.0) <= tol;
291 constexpr double negEigTol = 1e-12;
292 constexpr double zeroTol = 1e-15;
296 throw std::invalid_argument(
"VonNeumannEntropy: empty states are not supported");
299 const auto rho = ToDensityMatrix(s);
302 Eigen::MatrixXcd
H = 0.5 * (rho + rho.adjoint());
304 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> es(
H);
305 if (es.info() != Eigen::Success) {
306 throw std::runtime_error(
"VonNeumannEntropy: eigensolver failed");
309 const auto evals = es.eigenvalues();
311 double entropy = 0.0;
312 for (
int i = 0; i < evals.size(); ++i) {
313 double lambda = evals[i];
316 if (lambda > -negEigTol) {
319 throw std::runtime_error(
"VonNeumannEntropy: density matrix is not positive semidefinite");
323 if (lambda > zeroTol) {
324 entropy -= lambda * std::log2(lambda);
328 if (entropy < 0.0 && entropy > -1e-10) {
339 throw std::runtime_error(
"TraceDistance: states must have the same number of qubits");
344 throw std::invalid_argument(
"TraceDistance: empty states are not supported");
348 const bool bothKet =
dynamic_cast<const QStateKet*
>(&a) &&
dynamic_cast<const QStateKet*
>(&b);
349 const bool bothDm =
dynamic_cast<const QStateDM*
>(&a) &&
dynamic_cast<const QStateDM*
>(&b);
350 const bool bothStab =
dynamic_cast<const QStateStab*
>(&a) &&
dynamic_cast<const QStateStab*
>(&b);
352 if (!(bothKet || bothDm || bothStab)) {
353 throw std::runtime_error(
"TraceDistance: mixed backends are not supported. "
354 "Supported pairs are Ket-Ket, DM-DM, and Stab-Stab.");
357 const auto rho = ToDensityMatrix(a);
358 const auto sigma = ToDensityMatrix(b);
360 double d = 0.5 * HermitianTraceNorm(rho - sigma);
361 d = ClampToInterval(d, 0.0, 1.0);
Density-matrix concrete QState backend using qpp::cmat.
Ket-based concrete QState backend using qpp.
Stabilizer concrete QState backend using stab::AffineState.
Backend-agnostic interface for a quantum state object.
virtual std::size_t NumQubits() const =0
Return the number of logical qubits in the state.
double VonNeumannEntropy(const QState &s)
Compute the von Neumann entropy of a quantum state in bits.
double Purity(const QState &s)
Compute the purity of a quantum state.
double Fidelity(const QState &a, const QState &b)
Compute fidelity between two QState objects of the same backend type.
double TraceDistance(const QState &a, const QState &b)
Compute the trace distance between two QState objects of the same backend type.
bool IsPure(const QState &s, double tol)
Check whether a quantum state is pure within a numerical tolerance.
double UhlmannFidelity(const Eigen::MatrixXcd &rho, const Eigen::MatrixXcd &sigma, double negEigTol=1e-12)
Compute Uhlmann fidelity between two density matrices.
double HermitianTraceNorm(const Eigen::MatrixXcd &A)
Compute the trace norm of a Hermitian matrix.
Eigen::MatrixXcd HermitianSqrtPSD(const Eigen::MatrixXcd &A, double negEigTol=1e-12)
Compute the Hermitian square root of a positive semidefinite matrix.
double ClampToInterval(double x, double lo, double hi, double tol=1e-10)
Clamp a scalar metric to a closed interval within numerical tolerance.
qpp::cmat ToDensityMatrix(const QState &s)
Convert a supported QState backend to a density matrix.