2#if defined(INCLUDED_BY_FACTORY) && defined(__linux__)
9class GpuDensityMatrixSimulator :
public ImmediateGpuSimulator
12 GpuDensityMatrixSimulator()
14 configuration.SetConfiguration(
"method",
"density_matrix");
17 const char *MethodName()
const override
19 return "density_matrix";
22 void Initialize()
override
26 auto initializationLock = GpuLibrary::GetInstance()->LockInitialization();
27 const int gpuDevice = configuration.IsSet(
"gpu_device") ? Configuration::ParseGpuDevice(configuration.GetConfiguration(
"gpu_device"))
28 : SimulatorsFactory::ResolveGpuDevice();
29 configuration.SetConfiguration(
"gpu_device", std::to_string(gpuDevice));
30 if (!SimulatorsFactory::GetGpuLibrary(gpuDevice))
31 throw std::runtime_error(
"GpuState::Initialize: Unable to initialize GPU device " + std::to_string(gpuDevice));
33 densityMatrix = SimulatorsFactory::CreateGpuDensityMatrix(gpuDevice);
35 throw std::runtime_error(
"GpuState::Initialize: Failed to create the density matrix "
37 for (
const auto &[key, value] : configuration.GetConfigMap())
39 Configure(key.c_str(), value.c_str());
40 if (!densityMatrix->Create(nrQubits))
41 throw std::runtime_error(
"GpuState::Initialize: Failed to initialize the density matrix "
44 if (GetGpuDevice() != gpuDevice)
45 throw std::runtime_error(
"GpuState::Initialize: GPU plugin did not confirm the requested "
46 "device; update the GPU library");
50 void InitializeState(
size_t num_qubits, std::vector<std::complex<double>> &litudes)
override
55 nrQubits = num_qubits;
57 const bool created = densityMatrix->CreateWithState(nrQubits,
reinterpret_cast<const double *
>(amplitudes.data()));
59 throw std::runtime_error(
"GpuState::InitializeState: Failed to initialize the state.");
63 void InitializeState(
size_t num_qubits, AER::Vector<std::complex<double>> &litudes)
override
68 nrQubits = num_qubits;
70 const bool created = densityMatrix->CreateWithState(nrQubits,
reinterpret_cast<const double *
>(amplitudes.data()));
72 throw std::runtime_error(
"GpuState::InitializeState: Failed to initialize the state.");
77 void InitializeState(
size_t num_qubits, Eigen::VectorXcd &litudes)
override
82 nrQubits = num_qubits;
84 const bool created = densityMatrix->CreateWithState(nrQubits,
reinterpret_cast<const double *
>(amplitudes.data()));
86 throw std::runtime_error(
"GpuState::InitializeState: Failed to initialize the state.");
89 void InitializeToBasisState(
size_t num_qubits,
Types::qubit_t basisState)
override
94 nrQubits = num_qubits;
96 const bool created = densityMatrix->CreateWithBasisState(nrQubits,
static_cast<unsigned long long>(basisState));
98 throw std::runtime_error(
"GpuState::InitializeToBasisState: Failed to initialize the "
102 void InitializeToMixtureOfBasisStates(
size_t num_qubits,
const std::vector<std::pair<Types::qubit_t, double>> &mixture)
override
107 nrQubits = num_qubits;
109 std::vector<std::pair<unsigned long long, double>> converted;
110 converted.reserve(mixture.size());
111 for (
const auto &[basisState, weight] : mixture)
112 converted.emplace_back(
static_cast<unsigned long long>(basisState), weight);
113 const bool created = densityMatrix->CreateWithMixtureOfBasisStates(nrQubits, converted);
115 throw std::runtime_error(
"GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
119 void Reset()
override
122 densityMatrix->Reset();
123 upcomingGateIndex = 0;
126 void Configure(
const char *key,
const char *value)
override
130 if (std::string(
"gpu_device") == key)
132 const int device = Configuration::ParseGpuDevice(value);
133 if ((densityMatrix) && device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
134 throw std::invalid_argument(
"gpu_device cannot change after initialization; clear the "
136 configuration.SetConfiguration(key, std::to_string(device));
139 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
140 if (!svdGroup.empty())
142 Configuration::ParseGpuSvdFlag(value);
143 configuration.SetConfiguration(key, value);
146 if (std::string(
"method") == key)
148 ValidateMethod(value);
149 configuration.SetConfiguration(key, value);
152 if (std::string(key) ==
"precision" || std::string(key) ==
"use_double_precision")
156 const bool useDouble = Configuration::ParsePrecision(key, value);
157 if ((densityMatrix && densityMatrix->IsCreated()))
160 densityMatrix->SetDataType(useDouble);
161 configuration.SetConfiguration(key, value);
164 if (!configuration.WasApplied(key, value))
165 configuration.SetConfiguration(key, value);
166 if (std::string(
"seed") == key)
168 const uint64_t seed = std::stoull(value);
169 SeedAuxiliaryRng(seed);
172 densityMatrix->SetSeed(seed);
177 if (std::string(
"matrix_product_state_truncation_threshold") == key || std::string(
"matrix_product_operator_truncation_threshold") == key)
179 else if (std::string(
"matrix_product_state_max_bond_dimension") == key || std::string(
"matrix_product_operator_max_bond_dimension") == key)
187 if (std::string(key) ==
"precision" || std::string(key) ==
"use_double_precision")
189 const bool useDouble = densityMatrix ? densityMatrix->IsDoublePrecision() : configuration.GetConfiguration(
"precision") ==
"double";
190 return std::string(key) ==
"precision" ? (useDouble ?
"double" :
"single") : (useDouble ?
"1" :
"0");
194 if (std::string(
"method") == key)
196 return configuration.GetConfiguration(key);
203 const size_t oldNrQubits = nrQubits;
204 nrQubits += num_qubits;
208 void Clear()
override
210 densityMatrix =
nullptr;
212 upcomingGateIndex = 0;
213 upcomingGates.clear();
218 if (qubits.size() >
sizeof(
size_t) * 8)
219 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
220 "number of bits in the size_t type, the outcome will be undefined"
226 for (
size_t qubit : qubits)
228 if (densityMatrix->Measure(
static_cast<unsigned int>(qubit)))
234 NotifyObservers(qubits);
240 std::vector<bool> res(qubits.size(),
false);
243 for (
size_t i = 0; i < qubits.size(); ++i)
244 res[i] = densityMatrix->Measure(qubits[i]);
247 NotifyObservers(qubits);
255 for (
size_t qubit : qubits)
256 densityMatrix->ApplyReset(qubit);
259 NotifyObservers(qubits);
262 bool SupportsQuantumChannels()
const override
267 void ApplyQuantumChannel(
const Types::qubits_vector &targets,
const QuantumChannel &channel)
override
270 throw std::runtime_error(
"GPU quantum channels require an initialized density matrix or "
271 "matrix product operator");
272 if (targets.size() != channel.GetNumberOfQubits() || targets.empty() || targets.size() > 2)
273 throw std::invalid_argument(
"GPU density matrices and matrix product operators support one- "
274 "and two-qubit local channels");
275 std::vector<int> gpuTargets;
276 gpuTargets.reserve(targets.size());
277 for (
auto target : targets)
279 if (target >= nrQubits || std::find(gpuTargets.begin(), gpuTargets.end(), target) != gpuTargets.end())
280 throw std::invalid_argument(
"Invalid GPU quantum-channel target");
281 gpuTargets.push_back(
static_cast<int>(target));
283 const auto &kraus = channel.GetKrausOperators();
284 std::vector<double> interleaved;
285 interleaved.reserve(kraus.size() * kraus.front().size() * 2);
286 for (
const auto &op : kraus)
287 for (Eigen::Index i = 0; i < op.size(); ++i)
289 interleaved.push_back(op.data()[i].real());
290 interleaved.push_back(op.data()[i].imag());
292 const bool applied = densityMatrix->ApplyKraus(gpuTargets, kraus.size(), interleaved.data());
294 throw std::runtime_error(
"GPU density-matrix/matrix-product-operator channel application "
296 NotifyObservers(targets);
299 std::complex<double> DensityMatrixTrace()
const override
302 return densityMatrix->Trace();
303 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
304 "matrix_product_operator");
307 double DensityMatrixPurity()
const override
310 return densityMatrix->Purity();
311 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
312 "matrix_product_operator");
315 std::complex<double> DensityMatrixTraceOfSquare()
const override
319 const double tr = densityMatrix->Trace();
320 return densityMatrix->Purity() * tr * tr;
322 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
323 "matrix_product_operator");
326 std::complex<double> DensityMatrixOverlap(
const IState &other)
const override
328 const auto *rhs =
dynamic_cast<const GpuDensityMatrixSimulator *
>(&other);
330 throw std::invalid_argument(
"Density-matrix overlap requires matching GPU backends");
331 if (densityMatrix && rhs->densityMatrix)
332 return densityMatrix->HilbertSchmidtOverlap(*rhs->densityMatrix);
333 throw std::invalid_argument(
"Density-matrix overlap requires two density matrices or two MPOs");
336 double DensityMatrixHermiticityResidual()
const override
339 return densityMatrix->IsHermitian() ? 0. : std::numeric_limits<double>::infinity();
340 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
341 "matrix_product_operator");
344 bool IsDensityMatrixHermitian(
double eps = 1e-10)
const override
347 return densityMatrix->IsHermitian(eps);
348 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
349 "matrix_product_operator");
354 std::vector<int> keep(qubits.begin(), qubits.end());
355 const auto values = densityMatrix ? densityMatrix->PartialTrace(keep) :
throw std::runtime_error(
"GPU partial trace requires a mixed-state backend");
356 const Eigen::Index dim =
static_cast<Eigen::Index
>(
size_t{1} << keep.size());
357 return Eigen::Map<const Eigen::MatrixXcd>(values.data(), dim, dim);
360 double FidelityWithStatevector(
const Eigen::VectorXcd &psi)
const override
362 std::vector<double> raw(2 *
static_cast<size_t>(psi.size()));
363 for (Eigen::Index i = 0; i < psi.size(); ++i)
365 raw[2 * i] = psi[i].real();
366 raw[2 * i + 1] = psi[i].imag();
369 return densityMatrix->FidelityWithStatevector(raw.data());
370 throw std::runtime_error(
"GPU mixed-state fidelity requires density_matrix or "
371 "matrix_product_operator");
376 return densityMatrix->Probability(outcome);
381 throw std::runtime_error(
"GpuState::Amplitude: Amplitudes are not defined for density "
389 if (nrQubits >= std::numeric_limits<size_t>::digits)
390 throw std::length_error(
"Full probability enumeration exceeds the basis-index API; query "
391 "selected bit vectors");
392 const size_t numStates =
size_t{1} << nrQubits;
393 std::vector<double> result(numStates);
394 densityMatrix->AllProbabilities(result.data());
400 std::vector<double> result(qubits.size());
402 for (
size_t i = 0; i < qubits.size(); ++i)
403 result[i] = densityMatrix->Probability(qubits[i]);
410 if (qubits.empty() || shots == 0)
413 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
414 "number of bits in the Types::qubit_t type, the outcome will be "
417 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
420 std::vector<long int> samples(shots);
421 if (!densityMatrix->SampleAll(shots, samples.data()))
424 throw std::runtime_error(
"GpuState::SampleCounts: Density-matrix sampling failed.");
426 for (
auto outcome : samples)
429 for (
size_t i = 0; i < qubits.size(); ++i)
430 if (outcome & (1ULL << qubits[i]))
431 translatedOutcome |= 1ULL << i;
432 ++result[translatedOutcome];
436 NotifyObservers(qubits);
442 if (qubits.empty() || shots == 0)
447 std::vector<long int> samples(shots);
448 if (!densityMatrix->SampleAll(shots, samples.data()))
451 throw std::runtime_error(
"GpuState::SampleCountsMany: Density-matrix sampling failed.");
453 std::vector<bool> outcomeVec(qubits.size());
454 for (
auto outcome : samples)
456 for (
size_t i = 0; i < qubits.size(); ++i)
457 outcomeVec[i] = ((outcome >> qubits[i]) & 1) != 0;
458 ++result[outcomeVec];
462 NotifyObservers(qubits);
466 double ExpectationValue(
const std::string &pauliString)
override
469 result = densityMatrix->ExpectationValue(pauliString);
473 int GetGpuDevice()
const override
476 return densityMatrix->GetGpuDevice();
482 return SimulationType::kDensityMatrix;
485 void Flush()
override
487 const bool done = densityMatrix ? densityMatrix->Synchronize() :
true;
489 throw std::runtime_error(
"GpuState::Flush: device synchronization failed");
494 densityMatrix->SaveState();
499 densityMatrix->RestoreState();
504 std::vector<long int> samples(1);
505 if (!densityMatrix->SampleAll(1, samples.data()))
506 throw std::runtime_error(
"GpuState::MeasureNoCollapse: Density-matrix sampling failed.");
510 std::vector<bool> MeasureNoCollapseMany()
override
512 std::vector<long int> samples(1);
513 if (!densityMatrix->SampleAll(1, samples.data()))
514 throw std::runtime_error(
"GpuState::MeasureNoCollapseMany: Density-matrix sampling failed.");
515 std::vector<bool> result(nrQubits,
false);
516 for (
size_t i = 0; i < nrQubits; ++i)
517 result[i] = ((samples.front() >> i) & 1) != 0;
523 ApplyQuantumChannel({qubit}, QuantumChannel({gate}));
529 ApplyQuantumChannel({qubit0, qubit1}, QuantumChannel({gate}));
535 densityMatrix->ApplyP(qubit, lambda);
536 NotifyObservers({qubit});
541 densityMatrix->ApplyX(qubit);
542 NotifyObservers({qubit});
547 densityMatrix->ApplyY(qubit);
548 NotifyObservers({qubit});
553 densityMatrix->ApplyZ(qubit);
554 NotifyObservers({qubit});
559 densityMatrix->ApplyH(qubit);
560 NotifyObservers({qubit});
565 densityMatrix->ApplyS(qubit);
566 NotifyObservers({qubit});
571 densityMatrix->ApplySDG(qubit);
572 NotifyObservers({qubit});
577 densityMatrix->ApplyT(qubit);
578 NotifyObservers({qubit});
583 densityMatrix->ApplyTDG(qubit);
584 NotifyObservers({qubit});
589 densityMatrix->ApplySX(qubit);
590 NotifyObservers({qubit});
595 densityMatrix->ApplySXDG(qubit);
596 NotifyObservers({qubit});
601 densityMatrix->ApplyK(qubit);
602 NotifyObservers({qubit});
607 densityMatrix->ApplyRx(qubit, theta);
608 NotifyObservers({qubit});
613 densityMatrix->ApplyRy(qubit, theta);
614 NotifyObservers({qubit});
619 densityMatrix->ApplyRz(qubit, theta);
620 NotifyObservers({qubit});
623 void ApplyU(
Types::qubit_t qubit,
double theta,
double phi,
double lambda,
double gamma)
override
625 densityMatrix->ApplyU(qubit, theta, phi, lambda, gamma);
626 NotifyObservers({qubit});
631 densityMatrix->ApplyCX(ctrl_qubit, tgt_qubit);
632 NotifyObservers({tgt_qubit, ctrl_qubit});
637 densityMatrix->ApplyCY(ctrl_qubit, tgt_qubit);
638 NotifyObservers({tgt_qubit, ctrl_qubit});
643 densityMatrix->ApplyCZ(ctrl_qubit, tgt_qubit);
644 NotifyObservers({tgt_qubit, ctrl_qubit});
649 densityMatrix->ApplyCP(ctrl_qubit, tgt_qubit, lambda);
650 NotifyObservers({tgt_qubit, ctrl_qubit});
655 densityMatrix->ApplyCRx(ctrl_qubit, tgt_qubit, theta);
656 NotifyObservers({tgt_qubit, ctrl_qubit});
661 densityMatrix->ApplyCRy(ctrl_qubit, tgt_qubit, theta);
662 NotifyObservers({tgt_qubit, ctrl_qubit});
667 densityMatrix->ApplyCRz(ctrl_qubit, tgt_qubit, theta);
668 NotifyObservers({tgt_qubit, ctrl_qubit});
673 densityMatrix->ApplyCH(ctrl_qubit, tgt_qubit);
674 NotifyObservers({tgt_qubit, ctrl_qubit});
679 densityMatrix->ApplyCSX(ctrl_qubit, tgt_qubit);
680 NotifyObservers({tgt_qubit, ctrl_qubit});
685 densityMatrix->ApplyCSXDG(ctrl_qubit, tgt_qubit);
686 NotifyObservers({tgt_qubit, ctrl_qubit});
691 densityMatrix->ApplySwap(qubit0, qubit1);
692 NotifyObservers({qubit1, qubit0});
697 densityMatrix->ApplyCCX(qubit0, qubit1, qubit2);
698 NotifyObservers({qubit0, qubit1, qubit2});
703 densityMatrix->ApplyCSwap(ctrl_qubit, qubit0, qubit1);
704 NotifyObservers({qubit1, qubit0, ctrl_qubit});
709 densityMatrix->ApplyCU(ctrl_qubit, tgt_qubit, theta, phi, lambda, gamma);
710 NotifyObservers({tgt_qubit, ctrl_qubit});
713 std::unique_ptr<ISimulator> Clone()
override
715 auto cloned = std::make_unique<GpuDensityMatrixSimulator>();
716 cloned->nrQubits = nrQubits;
717 cloned->upcomingGates = upcomingGates;
718 cloned->upcomingGateIndex = upcomingGateIndex;
719 cloned->configuration = configuration;
722 cloned->densityMatrix = densityMatrix->Clone();
723 if (!cloned->densityMatrix)
724 throw std::runtime_error(
"GpuSimulator::Clone: Failed to clone density matrix state.");
726 if (configuration.IsSet(
"seed"))
727 cloned->SetSeed(DeriveSeed(std::stoull(configuration.GetConfiguration(
"seed")), nextSeedStream++));
732 std::unique_ptr<GpuDensityMatrix> densityMatrix;
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
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)
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 ApplyCY(void *sim, int controlQubit, int targetQubit)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
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)
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.