2#if defined(INCLUDED_BY_FACTORY)
3#include "DensityMatrix.h"
5#define _USE_MATH_DEFINES
12class SamplingDensityMatrix :
public QC::DensityMatrix<>
15 using QC::DensityMatrix<>::DensityMatrix;
17 double SamplingUniform()
24 snapshot = std::make_shared<const Eigen::MatrixXcd>(rho);
34 std::shared_ptr<const Eigen::MatrixXcd> snapshot;
38class QCSimDensityMatrixSimulator :
public QCSimGateSimulator
41 QCSimDensityMatrixSimulator()
43 configuration.SetConfiguration(
"method",
"density_matrix");
46 const char *MethodName()
const override
48 return "density_matrix";
51 void Initialize()
override
55 InitializeSamplingSeed();
57 densityMatrix = std::make_unique<SamplingDensityMatrix>(nrQubits);
58 SeedBackend(densityMatrix.get(), samplingSeed);
61 for (
const auto &[key, value] : configuration.GetConfigMap())
63 Configure(key.c_str(), value.c_str());
67 void InitializeState(
size_t num_qubits, std::vector<std::complex<double>> &litudes)
override
72 nrQubits = num_qubits;
74 Eigen::VectorXcd amplitudesEigen(Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(), amplitudes.size()));
75 densityMatrix->setFromStatevector(amplitudesEigen);
79 void InitializeState(
size_t num_qubits, AER::Vector<std::complex<double>> &litudes)
override
84 nrQubits = num_qubits;
86 Eigen::VectorXcd amplitudesEigen(Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(), amplitudes.size()));
87 densityMatrix->setFromStatevector(amplitudesEigen);
92 void InitializeState(
size_t num_qubits, Eigen::VectorXcd &litudes)
override
97 nrQubits = num_qubits;
99 densityMatrix->setFromStatevector(amplitudes);
102 void InitializeToBasisState(
size_t num_qubits,
Types::qubit_t basisState)
override
107 nrQubits = num_qubits;
109 densityMatrix->setToBasisState(
static_cast<size_t>(basisState));
112 void InitializeToMixtureOfBasisStates(
size_t num_qubits,
const std::vector<std::pair<Types::qubit_t, double>> &mixture)
override
117 nrQubits = num_qubits;
119 std::vector<std::pair<size_t, double>> converted;
120 converted.reserve(mixture.size());
121 for (
const auto &[basisState, weight] : mixture)
122 converted.emplace_back(
static_cast<size_t>(basisState), weight);
123 densityMatrix->setToMixtureOfBasisStates(converted);
126 void Reset()
override
129 densityMatrix->Reset();
130 upcomingGateIndex = 0;
133 void Configure(
const char *key,
const char *value)
override
135 if (ConfigureSampling(key, value))
137 if (std::string(
"method") == key)
139 ValidateMethod(value);
140 configuration.SetConfiguration(key, value);
143 if (!configuration.WasApplied(key, value))
144 configuration.SetConfiguration(key, value);
145 if (std::string(
"seed") == key)
147 const uint64_t seed = std::stoull(value);
148 SeedAuxiliaryRng(seed);
151 SeedBackend(densityMatrix.get(), seed);
154 if (std::string(key) ==
"pauli_propagator_workers")
156 const auto workers = configuration.GetConfigurationAsUnsigned(key);
158 throw std::invalid_argument(
"pauli_propagator_workers exceeds 1024");
166 const size_t oldNrQubits = nrQubits;
167 nrQubits += num_qubits;
171 void Clear()
override
173 densityMatrix =
nullptr;
175 upcomingGateIndex = 0;
176 upcomingGates.clear();
181 if (qubits.size() >
sizeof(
size_t) * 8)
182 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
183 "number of bits in the size_t type, the outcome will be undefined"
189 for (
size_t qubit : qubits)
191 if (densityMatrix->MeasureQubit(qubit))
197 NotifyObservers(qubits);
203 std::vector<bool> res(qubits.size(),
false);
206 for (
size_t q = 0; q < qubits.size(); ++q)
207 if (densityMatrix->MeasureQubit(qubits[q]))
211 NotifyObservers(qubits);
219 for (
size_t qubit : qubits)
220 densityMatrix->ApplyReset(qubit);
223 NotifyObservers(qubits);
226 bool SupportsQuantumChannels()
const override
231 void ApplyQuantumChannel(
const Types::qubits_vector &targets,
const QuantumChannel &channel)
override
233 if (!SupportsQuantumChannels())
234 throw std::runtime_error(
"QCSim quantum channels require density_matrix or "
235 "matrix_product_operator simulation");
236 if (targets.size() != channel.GetNumberOfQubits())
237 throw std::invalid_argument(
"The number of channel targets does not match its Kraus operators");
238 if (targets.empty() || targets.size() > 2)
239 throw std::invalid_argument(
"QCSim supports only one- and two-qubit local channels");
240 std::unordered_set<Types::qubit_t> uniqueTargets;
243 if (target >= nrQubits)
244 throw std::invalid_argument(
"Quantum-channel qubit is out of range");
245 if (!uniqueTargets.insert(target).second)
246 throw std::invalid_argument(
"Quantum-channel target qubits must be distinct");
248 const auto &krausOperators = channel.GetKrausOperators();
251 throw std::runtime_error(
"QCSim density-matrix state is not initialized");
252 if (targets.size() == 1)
253 densityMatrix->ApplyChannel(krausOperators, targets[0]);
255 densityMatrix->ApplyChannel(krausOperators, targets[0], targets[1]);
257 NotifyObservers(targets);
260 std::complex<double> DensityMatrixTrace()
const override
263 return densityMatrix->Trace();
264 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
265 "matrix_product_operator");
268 double DensityMatrixPurity()
const override
271 return densityMatrix->Purity();
272 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
273 "matrix_product_operator");
276 std::complex<double> DensityMatrixTraceOfSquare()
const override
280 const auto &rho = densityMatrix->getDensityMatrix();
281 return (rho * rho).trace();
283 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
284 "matrix_product_operator");
287 std::complex<double> DensityMatrixOverlap(
const IState &other)
const override
289 const auto *rhs =
dynamic_cast<const QCSimDensityMatrixSimulator *
>(&other);
291 throw std::invalid_argument(
"Density-matrix overlap requires matching QCSim backends");
292 if (densityMatrix && rhs->densityMatrix)
293 return densityMatrix->HilbertSchmidtOverlap(*rhs->densityMatrix);
294 throw std::invalid_argument(
"Density-matrix overlap requires two density matrices or two MPOs");
297 double DensityMatrixHermiticityResidual()
const override
300 return (densityMatrix->getDensityMatrix() - densityMatrix->getDensityMatrix().adjoint()).norm();
301 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
302 "matrix_product_operator");
305 bool IsDensityMatrixHermitian(
double eps = 1e-10)
const override
308 return densityMatrix->IsHermitian(eps);
309 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
310 "matrix_product_operator");
316 return densityMatrix->PartialTrace(std::vector<size_t>(qubits.begin(), qubits.end()));
317 throw std::runtime_error(
"Partial trace requires density_matrix or matrix_product_operator");
320 double FidelityWithStatevector(
const Eigen::VectorXcd &psi)
const override
323 return densityMatrix->FidelityWithStatevector(psi);
324 throw std::runtime_error(
"Mixed-state fidelity requires density_matrix or "
325 "matrix_product_operator");
330 return densityMatrix->getBasisStateProbability(outcome);
335 throw std::runtime_error(
"QCSimState::Amplitude: Amplitudes are not defined for the density "
336 "matrix simulator.");
341 const size_t nrBasisStates = densityMatrix->getNrBasisStates();
342 std::vector<double> result(nrBasisStates);
343 for (
size_t i = 0; i < nrBasisStates; ++i)
344 result[i] = densityMatrix->getBasisStateProbability(i);
350 std::vector<double> result(qubits.size());
352 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
353 result[i] = densityMatrix->getBasisStateProbability(qubits[i]);
360 if (!LegacySampling())
361 return SamplePrepared<false>(qubits, shots);
362 if (qubits.empty() || shots == 0)
364 if (qubits.size() >
sizeof(
size_t) * 8)
365 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
366 "number of bits in the size_t type, the outcome will be undefined"
368 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
371 const auto sampler = densityMatrix->PrepareSampler();
372 for (
size_t shot = 0; shot < shots; ++shot)
374 const size_t measured = densityMatrix->MeasureNoCollapse(sampler);
376 for (
size_t i = 0; i < qubits.size(); ++i)
377 if ((measured & (1ULL << qubits[i])) != 0)
383 NotifyObservers(qubits);
389 if (!LegacySampling())
390 return SamplePrepared<true>(qubits, shots);
391 if (qubits.empty() || shots == 0)
396 const auto sampler = densityMatrix->PrepareSampler();
397 std::vector<bool> packed(qubits.size());
398 for (
size_t shot = 0; shot < shots; ++shot)
400 const size_t measured = densityMatrix->MeasureNoCollapse(sampler);
401 for (
size_t i = 0; i < qubits.size(); ++i)
402 packed[i] = (measured & (1ULL << qubits[i])) != 0;
407 NotifyObservers(qubits);
411 double ExpectationValue(
const std::string &pauliStringOrig)
override
413 if (pauliStringOrig.empty())
415 std::string pauliString = pauliStringOrig;
420 const auto pauliOp = toupper(pauliString[i]);
421 if (pauliOp !=
'I' && pauliOp !=
'Z')
428 return densityMatrix->ExpectationValue(pauliString).real();
434 return SimulationType::kDensityMatrix;
439 densityMatrix->SaveState();
444 densityMatrix->RestoreState();
449 enableMultithreading = multithreading;
451 densityMatrix->SetMultithreading(multithreading);
457 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
458 "number of bits in the Types::qubit_t type, the outcome will be "
461 return densityMatrix->MeasureNoCollapse();
464 std::vector<bool> MeasureNoCollapseMany()
override
466 const auto measured = densityMatrix->MeasureNoCollapse();
467 std::vector<bool> res(nrQubits);
468 for (
size_t i = 0; i < nrQubits; ++i)
469 res[i] = ((measured >> i) & 1) == 1;
475 const QC::Gates::AppliedGate<> agate(gate, qubit);
476 ApplyNativeGate(agate);
477 NotifyObservers({qubit});
482 const QC::Gates::AppliedGate<> agate(gate, qubit0, qubit1);
483 ApplyNativeGate(agate);
484 NotifyObservers({qubit0, qubit1});
489 const QC::Gates::AppliedGate<> applied(gate, q0, q1, q2);
490 ApplyNativeGate(applied);
491 NotifyObservers({q0, q1, q2});
496 pgate.SetPhaseShift(lambda);
497 ApplyNativeGate(pgate,
static_cast<unsigned int>(qubit));
498 NotifyObservers({qubit});
503 ApplyNativeGate(xgate,
static_cast<unsigned int>(qubit));
504 NotifyObservers({qubit});
509 ApplyNativeGate(ygate,
static_cast<unsigned int>(qubit));
510 NotifyObservers({qubit});
515 ApplyNativeGate(zgate,
static_cast<unsigned int>(qubit));
516 NotifyObservers({qubit});
521 ApplyNativeGate(h,
static_cast<unsigned int>(qubit));
522 NotifyObservers({qubit});
527 ApplyNativeGate(sgate,
static_cast<unsigned int>(qubit));
528 NotifyObservers({qubit});
533 ApplyNativeGate(sdggate,
static_cast<unsigned int>(qubit));
534 NotifyObservers({qubit});
539 ApplyNativeGate(tgate,
static_cast<unsigned int>(qubit));
540 NotifyObservers({qubit});
545 ApplyNativeGate(tdggate,
static_cast<unsigned int>(qubit));
546 NotifyObservers({qubit});
551 ApplyNativeGate(sxgate,
static_cast<unsigned int>(qubit));
552 NotifyObservers({qubit});
557 ApplyNativeGate(sxdaggate,
static_cast<unsigned int>(qubit));
558 NotifyObservers({qubit});
563 ApplyNativeGate(k,
static_cast<unsigned int>(qubit));
564 NotifyObservers({qubit});
569 rxgate.SetTheta(theta);
570 ApplyNativeGate(rxgate,
static_cast<unsigned int>(qubit));
571 NotifyObservers({qubit});
576 rygate.SetTheta(theta);
577 ApplyNativeGate(rygate,
static_cast<unsigned int>(qubit));
578 NotifyObservers({qubit});
583 rzgate.SetTheta(theta);
584 ApplyNativeGate(rzgate,
static_cast<unsigned int>(qubit));
585 NotifyObservers({qubit});
588 void ApplyU(
Types::qubit_t qubit,
double theta,
double phi,
double lambda,
double gamma)
override
590 ugate.SetParams(theta, phi, lambda, gamma);
591 ApplyNativeGate(ugate,
static_cast<unsigned int>(qubit));
592 NotifyObservers({qubit});
597 ApplyNativeGate(cxgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
598 NotifyObservers({tgt_qubit, ctrl_qubit});
603 ApplyNativeGate(cygate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
604 NotifyObservers({tgt_qubit, ctrl_qubit});
609 ApplyNativeGate(czgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
610 NotifyObservers({tgt_qubit, ctrl_qubit});
615 cpgate.SetPhaseShift(lambda);
616 ApplyNativeGate(cpgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
617 NotifyObservers({tgt_qubit, ctrl_qubit});
622 crxgate.SetTheta(theta);
623 ApplyNativeGate(crxgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
624 NotifyObservers({tgt_qubit, ctrl_qubit});
629 crygate.SetTheta(theta);
630 ApplyNativeGate(crygate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
631 NotifyObservers({tgt_qubit, ctrl_qubit});
636 crzgate.SetTheta(theta);
637 ApplyNativeGate(crzgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
638 NotifyObservers({tgt_qubit, ctrl_qubit});
643 ApplyNativeGate(ch,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
644 NotifyObservers({tgt_qubit, ctrl_qubit});
649 ApplyNativeGate(csx,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
650 NotifyObservers({tgt_qubit, ctrl_qubit});
655 ApplyNativeGate(csxdag,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
656 NotifyObservers({tgt_qubit, ctrl_qubit});
661 ApplyNativeGate(swapgate,
static_cast<unsigned int>(qubit1),
static_cast<unsigned int>(qubit0));
662 NotifyObservers({qubit1, qubit0});
667 ApplyNativeGate(ccxgate,
static_cast<unsigned int>(qubit2),
static_cast<unsigned int>(qubit1),
static_cast<unsigned int>(qubit0));
668 NotifyObservers({qubit2, qubit1, qubit0});
673 ApplyNativeGate(cswapgate,
static_cast<unsigned int>(qubit1),
static_cast<unsigned int>(qubit0),
static_cast<unsigned int>(ctrl_qubit));
674 NotifyObservers({qubit1, qubit0, ctrl_qubit});
679 cugate.SetParams(theta, phi, lambda, gamma);
680 ApplyNativeGate(cugate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
681 NotifyObservers({tgt_qubit, ctrl_qubit});
684 std::unique_ptr<ISimulator> Clone()
override
686 return CloneForExecution(NextCloneSeed());
689 std::unique_ptr<ISimulator> CloneForExecution(uint64_t seed)
override
691 auto cloned = std::make_unique<QCSimDensityMatrixSimulator>();
692 cloned->nrQubits = nrQubits;
693 cloned->enableMultithreading = enableMultithreading;
694 cloned->upcomingGates = upcomingGates;
695 cloned->upcomingGateIndex = upcomingGateIndex;
697 cloned->densityMatrix = std::make_unique<SamplingDensityMatrix>(*densityMatrix);
698 for (
const auto &[key, value] : configuration.GetConfigMap())
699 cloned->Configure(key.c_str(), value.c_str());
702 cloned->SetSeed(seed);
707 std::unique_ptr<SamplingDensityMatrix> densityMatrix;
712 if (qubits.empty() || !shots)
715 const size_t states = densityMatrix->getNrBasisStates();
716 std::vector<size_t> selected, positions;
717 for (
const auto q : qubits)
719 const auto found = std::find(selected.begin(), selected.end(), q);
720 positions.push_back(
static_cast<size_t>(found - selected.begin()));
721 if (found == selected.end())
722 selected.push_back(q);
724 const bool marginal = selected.size() < nrQubits && selected.size() <= 12 && shots >= std::max<size_t>(256, states / 16);
725 const size_t outcomes = marginal ?
size_t{1} << selected.size() : states;
726 auto options = SamplingOptions();
727 if (outcomes > (options.memoryBytes - 4096) /
sizeof(
double))
728 throw std::length_error(
"Density sampling snapshot exceeds the scratch budget");
729 options.memoryBytes -= outcomes *
sizeof(double);
730 std::vector<double> probabilities(outcomes, 0.);
731 const auto &matrix = densityMatrix->getDensityMatrix();
733 for (
size_t state = 0; state < states; ++state)
735 const auto diagonal = matrix(state, state);
736 if (!std::isfinite(diagonal.real()) || !std::isfinite(diagonal.imag()) || std::abs(diagonal.imag()) > 1E-10 || diagonal.real() < -1E-12)
737 throw std::domain_error(
"Density-matrix populations must be finite, real and nonnegative");
738 const double probability = std::max(0., diagonal.real());
744 for (
size_t bit = 0; bit < selected.size(); ++bit)
745 if ((state >> selected[bit]) & 1)
746 row |=
size_t{1} << bit;
748 probabilities[row] += probability;
750 if (!std::isfinite(mass) || mass <= 1E-20)
751 throw std::domain_error(
"Cannot sample a density matrix with no probability mass");
752 const Utils::Sampling::Prepared plan(
753 outcomes, shots, [&](
size_t row) {
return probabilities[row]; }, options);
755 plan, shots, qubits.size(), [&](
size_t row,
size_t bit) { return ((row >> (marginal ? positions[bit] : qubits[bit])) & 1) != 0; },
756 [&] { return densityMatrix->SamplingUniform(); });
757 NotifyObservers(qubits);
761 template <
class Gate,
class...
Qubits>
void ApplyNativeGate(
const Gate &gate,
Qubits... qubits)
767 struct RestoreThreading
769 SamplingDensityMatrix &state;
774 state.SetMultithreading(enabled);
776 } restore{*densityMatrix, densityMatrix->GetMultithreading()};
778 bool hasTeam =
false;
780 hasTeam = !omp_in_parallel() && omp_get_max_threads() > 1;
782 const bool trajectory = configuration.GetConfiguration(
"reproducible_trajectory") ==
"true";
783 if (!LegacySampling() && (!hasTeam || (trajectory && gate.getStructure().kind == QC::Gates::GateStructure::Kind::Diagonal)))
784 densityMatrix->SetMultithreading(
false);
785 densityMatrix->ApplyGate(gate,
static_cast<size_t>(qubits)...);
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
unsigned long int GetNumberOfQubits(void *sim)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyCSwap(void *sim, int controlQubit, int qubit1, int qubit2)
int ApplyCCX(void *sim, int controlQubit1, int controlQubit2, int targetQubit)
int ApplyY(void *sim, int qubit)
double * Amplitude(void *sim, unsigned long long int outcome)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyGenericThreeQubitGate(void *sim, unsigned long q0, unsigned long q1, unsigned long q2, const double *buffer)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int SetMultithreading(void *sim, int multithreading)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int ApplyCH(void *sim, int controlQubit, int targetQubit)
int GetSimulationType(void *sim)
unsigned long long int * SampleCounts(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits, unsigned long int shots)
int ApplyCZ(void *sim, int controlQubit, int targetQubit)
int ApplyRz(void *sim, int qubit, double theta)
int ApplyT(void *sim, int qubit)
int ApplyCRx(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyGenericOneQubitGate(void *sim, unsigned long q0, const double *buffer)
int ApplyGenericTwoQubitGate(void *sim, unsigned long q0, unsigned long q1, const double *buffer)
std::vector< int > Qubits(const Types::qubits_vector &qubits, size_t width)
SimulationType
The type of simulation.
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
uint_fast64_t qubit_t
The type of a qubit.
double Uniform(std::mt19937_64 &engine)
Counts< Many > Count(const Plan &plan, size_t shots, size_t bits, const Bit &bit, Uniform &&uniform)
void ValidateQubits(const Qubits &qubits, size_t width, bool many)
std::unordered_map< std::conditional_t< Many, std::vector< bool >, Types::qubit_t >, Types::qubit_t > Counts