2#if defined(INCLUDED_BY_FACTORY) && defined(__linux__)
9class GpuMPOSimulator :
public GpuTensorChainSimulator
12 GpuMPOSimulator() : GpuTensorChainSimulator(true)
14 configuration.SetConfiguration(
"method",
"matrix_product_operator");
15 configuration.SetConfiguration(
"matrix_product_state_max_bond_dimension",
"128");
16 configuration.SetConfiguration(
"matrix_product_operator_max_bond_dimension",
"128");
19 const char *MethodName()
const override
21 return "matrix_product_operator";
24 void Initialize()
override
26 RefreshRoutingSettings();
27 InvalidateDummyHistory();
30 auto initializationLock = GpuLibrary::GetInstance()->LockInitialization();
31 const int gpuDevice = configuration.IsSet(
"gpu_device") ? Configuration::ParseGpuDevice(configuration.GetConfiguration(
"gpu_device"))
32 : SimulatorsFactory::ResolveGpuDevice();
33 configuration.SetConfiguration(
"gpu_device", std::to_string(gpuDevice));
34 if (!SimulatorsFactory::GetGpuLibrary(gpuDevice))
35 throw std::runtime_error(
"GpuState::Initialize: Unable to initialize GPU device " + std::to_string(gpuDevice));
36 SetSwapGatesRelabelQubits(GpuLibrary::GetInstance()->MPOSwapRelabels());
38 mpo = SimulatorsFactory::CreateGpuMPO(gpuDevice);
40 throw std::runtime_error(
"GpuState::Initialize: Failed to create the matrix product "
42 mpo->SetCallbackContext(
static_cast<GpuTensorChainSimulator *
>(
this));
44 mpo->InstallBondSummary(&GpuTensorChainSimulator::BondSummaryCallback, &GpuTensorChainSimulator::BondDimCallback);
45 for (
const auto &[key, value] : configuration.GetConfigMap())
47 Configure(key.c_str(), value.c_str());
48 if (!mpo->Create(nrQubits))
49 throw std::runtime_error(
"GpuState::Initialize: Failed to initialize the matrix product "
51 if (!useOptimalMeetingPosition)
52 mpo->SetUseOptimalMeetingPosition(
false);
54 if (GetGpuDevice() != gpuDevice)
55 throw std::runtime_error(
"GpuState::Initialize: GPU plugin did not confirm the requested "
56 "device; update the GPU library");
60 void InitializeState(
size_t num_qubits, std::vector<std::complex<double>> &litudes)
override
65 nrQubits = num_qubits;
67 const bool created = mpo->CreateWithState(nrQubits,
reinterpret_cast<const double *
>(amplitudes.data()));
69 throw std::runtime_error(
"GpuState::InitializeState: Failed to initialize the state.");
73 void InitializeState(
size_t num_qubits, AER::Vector<std::complex<double>> &litudes)
override
78 nrQubits = num_qubits;
80 const bool created = mpo->CreateWithState(nrQubits,
reinterpret_cast<const double *
>(amplitudes.data()));
82 throw std::runtime_error(
"GpuState::InitializeState: Failed to initialize the state.");
87 void InitializeState(
size_t num_qubits, Eigen::VectorXcd &litudes)
override
92 nrQubits = num_qubits;
94 const bool created = mpo->CreateWithState(nrQubits,
reinterpret_cast<const double *
>(amplitudes.data()));
96 throw std::runtime_error(
"GpuState::InitializeState: Failed to initialize the state.");
99 void InitializeToBasisState(
size_t num_qubits,
Types::qubit_t basisState)
override
104 nrQubits = num_qubits;
106 const bool created = mpo->CreateWithBasisState(nrQubits,
static_cast<unsigned long long>(basisState));
108 throw std::runtime_error(
"GpuState::InitializeToBasisState: Failed to initialize the "
112 void InitializeToBasisState(
size_t num_qubits,
const std::vector<bool> &basisState)
override
117 nrQubits = num_qubits;
121 std::vector<unsigned char> stateBits(num_qubits, 0);
122 for (
size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
123 stateBits[q] = basisState[q] ? 1 : 0;
124 created = mpo->CreateWithBasisStateBits(nrQubits, stateBits);
127 throw std::runtime_error(
"GpuState::InitializeToBasisState: Failed to initialize the "
131 void InitializeToMixtureOfBasisStates(
size_t num_qubits,
const std::vector<std::pair<Types::qubit_t, double>> &mixture)
override
136 nrQubits = num_qubits;
138 std::vector<std::pair<unsigned long long, double>> converted;
139 converted.reserve(mixture.size());
140 for (
const auto &[basisState, weight] : mixture)
141 converted.emplace_back(
static_cast<unsigned long long>(basisState), weight);
142 const bool created = mpo->CreateWithMixtureOfBasisStates(nrQubits, converted);
144 throw std::runtime_error(
"GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
148 void InitializeToMixtureOfBasisStates(
size_t num_qubits,
const std::vector<std::pair<std::vector<bool>,
double>> &mixture)
override
153 nrQubits = num_qubits;
155 std::vector<double> weights;
156 weights.reserve(mixture.size());
157 std::vector<unsigned char> stateBitsFlat;
158 stateBitsFlat.reserve(mixture.size() * num_qubits);
159 for (
const auto &[basisState, weight] : mixture)
161 weights.push_back(weight);
162 for (
size_t q = 0; q < num_qubits; ++q)
163 stateBitsFlat.push_back((q < basisState.size() && basisState[q]) ? 1 : 0);
165 if (!mpo->CreateWithMixtureOfBasisStatesBits(nrQubits, stateBitsFlat, weights))
166 throw std::runtime_error(
"GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
170 void Reset()
override
177 upcomingGateIndex = 0;
178 ResetDummySimulator();
181 void SetInitialQubitsMap(
const std::vector<long long int> &initialMap)
override
185 mpo->SetInitialQubitsMap(initialMap);
186 EnsureDummySimulator(initialMap.size());
187 dummySim->SetInitialQubitsMap(initialMap);
191 void SetUseOptimalMeetingPosition(
bool enable)
override
193 useOptimalMeetingPosition = enable;
194 RefreshRoutingSettings();
196 mpo->SetUseOptimalMeetingPosition(enable);
197 RefreshRoutingCallback();
200 void Configure(
const char *key,
const char *value)
override
204 if (std::string(
"gpu_device") == key)
206 const int device = Configuration::ParseGpuDevice(value);
207 if ((mpo) && device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
208 throw std::invalid_argument(
"gpu_device cannot change after initialization; clear the "
210 configuration.SetConfiguration(key, std::to_string(device));
213 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
214 if (!svdGroup.empty())
216 const bool enabled = Configuration::ParseGpuSvdFlag(value);
217 const bool gesvd = svdGroup == key;
218 const char algorithm = std::string(key).back();
219 const auto apply = [algorithm, enabled](
auto &backend) {
222 if (algorithm ==
'j')
223 return backend->SetGesvdJ(enabled);
224 if (algorithm ==
'p')
225 return backend->SetGesvdP(enabled);
226 return backend->SetGesvdR(enabled);
228 const auto applyGesvd = [enabled](
auto &backend) {
229 if (!enabled || !backend)
231 return backend->SetGesvdJ(
false) && backend->SetGesvdP(
false) && backend->SetGesvdR(
false);
234 if (svdGroup ==
"matrix_product_operator_use_gesvd")
235 applied = gesvd ? applyGesvd(mpo) : apply(mpo);
237 throw std::runtime_error(std::string(
"GPU library cannot apply ") + key +
"; an updated GPU plugin may be required");
238 configuration.SetConfiguration(key, value);
241 if (std::string(
"method") == key)
243 ValidateMethod(value);
244 configuration.SetConfiguration(key, value);
247 if (std::string(key) ==
"precision" || std::string(key) ==
"use_double_precision")
251 const bool useDouble = Configuration::ParsePrecision(key, value);
252 if ((mpo && mpo->IsCreated()))
255 mpo->SetDataType(useDouble);
256 configuration.SetConfiguration(key, value);
259 if (!configuration.WasApplied(key, value))
260 configuration.SetConfiguration(key, value);
261 RefreshRoutingSettings();
262 if (std::string(
"seed") == key)
264 const uint64_t seed = std::stoull(value);
265 SeedAuxiliaryRng(seed);
271 if (std::string(
"matrix_product_state_truncation_threshold") == key || std::string(
"matrix_product_operator_truncation_threshold") == key)
273 const double singularValueThreshold = std::stod(value);
274 if (singularValueThreshold >= 0.)
277 mpo->SetCutoff(singularValueThreshold);
280 else if (std::string(
"matrix_product_state_truncation_mode") == key || std::string(
"matrix_product_operator_truncation_mode") == key)
282 int truncationMode = -1;
283 if (std::string(
"relative_max") == value)
285 else if (std::string(
"discarded_weight") == value)
287 if (truncationMode >= 0)
290 mpo->SetTruncationMode(truncationMode);
293 else if (std::string(
"matrix_product_state_max_bond_dimension") == key || std::string(
"matrix_product_operator_max_bond_dimension") == key)
295 const long long int chi = std::stoi(value);
297 const std::string bondDimension(value);
298 for (
const char *alias : {
"matrix_product_state_max_bond_dimension",
"matrix_product_operator_max_bond_dimension"})
299 if (!configuration.WasApplied(alias, bondDimension))
300 configuration.SetConfiguration(alias, bondDimension);
302 RefreshRoutingSettings();
304 mpo->SetMaxExtent(chi);
308 dummySim->SetMaxBondDimension(EffectiveLookaheadMaxBondDimension(dummySim->getNrQubits(), chi));
311 else if (std::string(
"matrix_product_operator_kraus_completeness_check") == key)
314 if (std::string(value) ==
"ignore")
316 else if (std::string(value) ==
"warn")
318 else if (std::string(value) ==
"strict")
320 if (mpo && mode >= 0 && !mpo->SetKrausCompletenessCheck(mode))
321 throw std::runtime_error(
"Invalid GPU MPO Kraus completeness mode");
323 else if (std::string(
"matrix_product_operator_restore_trace_after_truncation") == key)
325 if (mpo && !mpo->SetRestoreTraceAfterTruncation(std::string(value) ==
"true" || std::string(value) ==
"1"))
326 throw std::runtime_error(
"GPU MPO truncation configuration failed");
328 else if (std::string(
"matrix_product_operator_hermitize_after_truncation") == key)
330 if (mpo && !mpo->SetHermitizeAfterTruncation(std::string(value) ==
"true" || std::string(value) ==
"1"))
331 throw std::runtime_error(
"GPU MPO truncation configuration failed");
339 if (std::string(key) ==
"precision" || std::string(key) ==
"use_double_precision")
341 const bool useDouble = mpo ? mpo->IsDoublePrecision() : configuration.GetConfiguration(
"precision") ==
"double";
342 return std::string(key) ==
"precision" ? (useDouble ?
"double" :
"single") : (useDouble ?
"1" :
"0");
346 if (std::string(key) ==
"matrix_product_operator_restore_trace_after_truncation")
347 return mpo->GetRestoreTraceAfterTruncation() ?
"true" :
"false";
348 if (std::string(key) ==
"matrix_product_operator_hermitize_after_truncation")
349 return mpo->GetHermitizeAfterTruncation() ?
"true" :
"false";
353 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
354 if (!svdGroup.empty())
358 const auto readGesvd = [](
const auto &backend) {
return !backend->GetGesvdJ() && !backend->GetGesvdP() && !backend->GetGesvdR(); };
359 if (svdGroup ==
"matrix_product_operator_use_gesvd" && mpo)
360 return readGesvd(mpo) ?
"true" :
"false";
362 const char algorithm = std::string(key).back();
363 const auto read = [algorithm](
const auto &backend) {
364 if (algorithm ==
'j')
365 return backend->GetGesvdJ();
366 if (algorithm ==
'p')
367 return backend->GetGesvdP();
368 return backend->GetGesvdR();
370 if (svdGroup ==
"matrix_product_operator_use_gesvd" && mpo)
371 return read(mpo) ?
"true" :
"false";
373 if (std::string(
"method") == key)
375 return configuration.GetConfiguration(key);
382 const size_t oldNrQubits = nrQubits;
383 nrQubits += num_qubits;
387 void Clear()
override
391 ClearDummySimulator();
392 upcomingGateIndex = 0;
393 upcomingGates.clear();
398 InvalidateDummyHistory();
399 if (mpo && qubits.size() >
sizeof(
size_t) * 8)
400 throw std::invalid_argument(
"Use MeasureMany for more than 64 measured qubits");
401 if (qubits.size() >
sizeof(
size_t) * 8)
402 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
403 "number of bits in the size_t type, the outcome will be undefined"
409 for (
size_t qubit : qubits)
411 if (mpo->Measure(
static_cast<unsigned int>(qubit)))
417 NotifyObservers(qubits);
423 InvalidateDummyHistory();
424 std::vector<bool> res(qubits.size(),
false);
427 for (
size_t i = 0; i < qubits.size(); ++i)
428 res[i] = mpo->Measure(
static_cast<unsigned int>(qubits[i]));
431 NotifyObservers(qubits);
437 InvalidateDummyHistory();
440 for (
size_t qubit : qubits)
441 mpo->ApplyReset(
static_cast<int>(qubit));
444 NotifyObservers(qubits);
447 bool SupportsQuantumChannels()
const override
452 void ApplyQuantumChannel(
const Types::qubits_vector &targets,
const QuantumChannel &channel)
override
455 throw std::runtime_error(
"GPU quantum channels require an initialized density matrix or "
456 "matrix product operator");
457 if (targets.size() != channel.GetNumberOfQubits() || targets.empty() || targets.size() > 2)
458 throw std::invalid_argument(
"GPU density matrices and matrix product operators support one- "
459 "and two-qubit local channels");
460 std::vector<int> gpuTargets;
461 gpuTargets.reserve(targets.size());
462 for (
auto target : targets)
464 if (target >= nrQubits || std::find(gpuTargets.begin(), gpuTargets.end(), target) != gpuTargets.end())
465 throw std::invalid_argument(
"Invalid GPU quantum-channel target");
466 gpuTargets.push_back(
static_cast<int>(target));
468 const auto &kraus = channel.GetKrausOperators();
469 std::vector<double> interleaved;
470 interleaved.reserve(kraus.size() * kraus.front().size() * 2);
471 for (
const auto &op : kraus)
472 for (Eigen::Index i = 0; i < op.size(); ++i)
474 interleaved.push_back(op.data()[i].real());
475 interleaved.push_back(op.data()[i].imag());
479 InvalidateDummyHistory();
480 const bool applied = mpo->ApplyKraus(gpuTargets, kraus.size(), interleaved.data());
482 throw std::runtime_error(
"GPU density-matrix/matrix-product-operator channel application "
484 InvalidateDummyHistory();
485 NotifyObservers(targets);
488 double ProbabilityBits(
const std::vector<bool> &bits)
override
491 return IState::ProbabilityBits(bits);
492 return mpo->ProbabilityBits(std::vector<unsigned char>(bits.begin(), bits.end()));
495 std::complex<double> DensityMatrixElementBits(
const std::vector<bool> &row,
const std::vector<bool> &col)
const override
498 return IState::DensityMatrixElementBits(row, col);
499 return mpo->GetElementBits(std::vector<unsigned char>(row.begin(), row.end()), std::vector<unsigned char>(col.begin(), col.end()));
502 Eigen::MatrixXcd GetDensityMatrix(
bool normalized =
true)
const override
505 return IState::GetDensityMatrix(normalized);
506 const auto values = mpo->GetDensityMatrix(normalized);
507 const Eigen::Index dim = Eigen::Index{1} << nrQubits;
508 return Eigen::Map<const Eigen::MatrixXcd>(values.data(), dim, dim);
511 std::complex<double> ExpectationValueComplex(
const std::string &pauli,
bool normalized =
true)
const override
514 return IState::ExpectationValueComplex(pauli, normalized);
515 MPOValidation::Pauli(pauli, nrQubits);
516 return mpo->ExpectationValueComplex(pauli, normalized);
519 std::vector<std::complex<double>> ExpectationValuesComplex(
const std::vector<std::string> &paulis,
bool normalized =
true)
const override
522 return IState::ExpectationValuesComplex(paulis, normalized);
523 for (
const auto &pauli : paulis)
524 MPOValidation::Pauli(pauli, nrQubits);
525 return mpo->ExpectationValuesComplex(paulis, normalized);
528 void ApplyOperator(
const Types::qubits_vector &qubits,
const Eigen::MatrixXcd &matrix,
bool normalize =
false)
override
531 return IState::ApplyOperator(qubits, matrix, normalize);
532 const auto selected = MPOValidation::Operator(qubits, matrix, nrQubits);
533 if (!normalize && swapGatesRelabelQubits && qubits.size() == 2 && IsExactSwapMatrix(matrix))
535 std::vector<double> raw(2 * matrix.size());
536 for (Eigen::Index i = 0; i < matrix.size(); ++i)
538 raw[2 * i] = matrix.data()[i].real();
539 raw[2 * i + 1] = matrix.data()[i].imag();
543 InvalidateDummyHistory();
544 if (!mpo->ApplyOperator(selected, raw.data(), normalize))
545 throw std::runtime_error(
"GPU MPO operator application failed");
546 InvalidateDummyHistory();
547 NotifyObservers(qubits);
553 return IState::MoveAtBeginningOfChain(qubits);
554 const auto selected = MPOValidation::Qubits(qubits, nrQubits);
555 if (selected.empty())
557 mpo->MoveAtBeginningOfChain(selected);
558 InvalidateDummyHistory();
561 std::complex<double> DensityMatrixTrace()
const override
564 return mpo->TraceComplex();
565 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
566 "matrix_product_operator");
569 double DensityMatrixPurity()
const override
572 return mpo->Purity();
573 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
574 "matrix_product_operator");
577 std::complex<double> DensityMatrixTraceOfSquare()
const override
580 return mpo->TraceOfSquare();
581 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
582 "matrix_product_operator");
585 std::complex<double> DensityMatrixOverlap(
const IState &other)
const override
587 const auto *rhs =
dynamic_cast<const GpuMPOSimulator *
>(&other);
589 throw std::invalid_argument(
"Density-matrix overlap requires matching GPU backends");
591 return mpo->HilbertSchmidtOverlap(*rhs->mpo);
592 throw std::invalid_argument(
"Density-matrix overlap requires two density matrices or two MPOs");
595 double DensityMatrixHermiticityResidual()
const override
598 return mpo->HermiticityResidual();
599 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
600 "matrix_product_operator");
603 bool IsDensityMatrixHermitian(
double eps = 1e-10)
const override
606 return mpo->IsHermitian(eps);
607 throw std::runtime_error(
"GPU mixed-state diagnostics require density_matrix or "
608 "matrix_product_operator");
613 std::vector<int> keep(qubits.begin(), qubits.end());
614 const auto values = mpo ? mpo->PartialTrace(keep) :
throw std::runtime_error(
"GPU partial trace requires a mixed-state backend");
615 const Eigen::Index dim =
static_cast<Eigen::Index
>(
size_t{1} << keep.size());
616 return Eigen::Map<const Eigen::MatrixXcd>(values.data(), dim, dim);
619 double FidelityWithStatevector(
const Eigen::VectorXcd &psi)
const override
621 std::vector<double> raw(2 *
static_cast<size_t>(psi.size()));
622 for (Eigen::Index i = 0; i < psi.size(); ++i)
624 raw[2 * i] = psi[i].real();
625 raw[2 * i + 1] = psi[i].imag();
628 return mpo->FidelityWithStatevector(raw.data());
629 throw std::runtime_error(
"GPU mixed-state fidelity requires density_matrix or "
630 "matrix_product_operator");
633 void RestoreDensityMatrixTrace()
override
636 throw std::runtime_error(
"Trace restoration is only available for GPU MPO");
640 void HermitizeDensityMatrix()
override
643 throw std::runtime_error(
"Hermitization is only available for GPU MPO");
649 InvalidateDummyHistory();
653 throw std::runtime_error(
"Trim is only available for GPU MPS and MPO");
656 void ReCanonicalize()
override
658 InvalidateDummyHistory();
660 mpo->ReCanonicalize();
662 throw std::runtime_error(
"Canonicalization is only available for GPU MPS and MPO");
668 throw std::out_of_range(
"GPU MPO basis state is out of range");
669 std::vector<bool> bits(nrQubits);
670 for (
size_t q = 0; q < std::min(nrQubits,
sizeof(
Types::qubit_t) * 8); ++q)
671 bits[q] = ((outcome >> q) & 1) != 0;
672 return ProbabilityBits(bits);
677 throw std::runtime_error(
"GpuState::Amplitude: Amplitudes are not defined for matrix "
678 "product operators.");
685 if (nrQubits >= std::numeric_limits<size_t>::digits || (mpo && nrQubits >= 63))
686 throw std::length_error(
"Full probability enumeration exceeds the basis-index API; query "
687 "selected bit vectors");
688 const size_t numStates =
size_t{1} << nrQubits;
689 std::vector<double> result(numStates);
690 mpo->AllProbabilities(result.data());
696 std::vector<double> result(qubits.size());
698 for (
size_t i = 0; i < qubits.size(); ++i)
709 throw std::invalid_argument(
"Use SampleCountsMany for more than 64 measured qubits");
710 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
711 for (
const auto &entry : SampleCountsMany(qubits, shots))
714 for (
size_t i = 0; i < entry.first.size(); ++i)
717 result[value] += entry.second;
721 if (qubits.empty() || shots == 0)
724 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
725 "number of bits in the Types::qubit_t type, the outcome will be "
728 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
731 NotifyObservers(qubits);
739 std::vector<unsigned int> selected;
740 for (
auto q : qubits)
742 if (q >= nrQubits || q >
static_cast<size_t>(std::numeric_limits<int>::max()))
743 throw std::out_of_range(
"GPU tensor sampled qubit is out of range");
744 selected.push_back(
static_cast<unsigned int>(q));
746 if (qubits.empty() || shots == 0)
748 std::sort(selected.begin(), selected.end());
749 selected.erase(std::unique(selected.begin(), selected.end()), selected.end());
750 const auto counts = mpo->SampleHistogram(shots, std::move(selected));
753 ValidateDummyHistoryMap();
754 const auto positions = SampleBitPositions(qubits);
756 for (
const auto &entry : counts)
758 std::vector<bool> bits(qubits.size());
759 for (
size_t i = 0; i < bits.size(); ++i)
760 bits[i] = entry.first[positions[i]];
761 result[std::move(bits)] += entry.second;
763 NotifyObservers(qubits);
766 if (qubits.empty() || shots == 0)
771 NotifyObservers(qubits);
775 double ExpectationValue(
const std::string &pauliString)
override
778 result = mpo->ExpectationValue(pauliString);
782 std::vector<double> ExpectationValues(
const std::vector<std::string> &paulis)
override
785 return IState::ExpectationValues(paulis);
786 std::vector<double> result(paulis.size(), 0.);
787 std::vector<std::string> selected;
788 std::vector<size_t> indices;
789 for (
size_t i = 0; i < paulis.size(); ++i)
791 if (paulis[i].empty())
796 if (paulis[i].size() > nrQubits)
798 auto pauli = paulis[i];
799 for (
char &p : pauli)
800 p =
static_cast<char>(std::toupper(
static_cast<unsigned char>(p)));
801 if (pauli.find_first_not_of(
"IXYZ") != std::string::npos)
803 indices.push_back(i);
804 selected.push_back(std::move(pauli));
806 if (selected.empty())
808 const auto values = mpo->ExpectationValues(selected);
809 for (
size_t i = 0; i < indices.size(); ++i)
810 result[indices[i]] = values[i];
814 int GetGpuDevice()
const override
817 return mpo->GetGpuDevice();
823 return SimulationType::kMatrixProductOperator;
841 throw std::invalid_argument(
"Use MeasureNoCollapseMany for more than 64 qubits");
843 std::vector<long int> samples(1);
844 if (!mpo->SampleAll(1, samples.data()))
845 throw std::runtime_error(
"GpuState::MeasureNoCollapse: Matrix-product-operator sampling "
851 std::vector<bool> MeasureNoCollapseMany()
override
853 std::vector<int> qubits(nrQubits);
854 std::iota(qubits.begin(), qubits.end(), 0);
855 const auto bits = mpo->SampleBits(1, qubits);
856 return std::vector<bool>(bits.begin(), bits.end());
861 if (!mpo->ApplyOneQubitMatrix(
static_cast<int>(qubit),
reinterpret_cast<const double *
>(gate.data())))
862 throw std::runtime_error(
"GpuSimulator::ApplyGenericOneQubitGate: Failed to apply the "
863 "generic one-qubit gate on the matrix product operator "
865 NotifyObservers({qubit});
871 if (!mpo->ApplyTwoQubitMatrix(
static_cast<int>(qubit0),
static_cast<int>(qubit1),
reinterpret_cast<const double *
>(gate.data())))
872 throw std::runtime_error(
"GpuSimulator::ApplyGenericTwoQubitGate: Failed to apply the "
873 "generic two-qubit gate on the matrix product operator "
875 RecordExecutedTwoQubitGate(qubit0, qubit1, swapGatesRelabelQubits && IsExactSwapMatrix(gate),
EffectiveSchmidtRank(gate));
876 NotifyObservers({qubit0, qubit1});
882 mpo->ApplyP(qubit, lambda);
883 NotifyObservers({qubit});
889 NotifyObservers({qubit});
895 NotifyObservers({qubit});
901 NotifyObservers({qubit});
907 NotifyObservers({qubit});
913 NotifyObservers({qubit});
918 mpo->ApplySDG(qubit);
919 NotifyObservers({qubit});
925 NotifyObservers({qubit});
930 mpo->ApplyTDG(qubit);
931 NotifyObservers({qubit});
937 NotifyObservers({qubit});
942 mpo->ApplySXDG(qubit);
943 NotifyObservers({qubit});
949 NotifyObservers({qubit});
954 mpo->ApplyRx(qubit, theta);
955 NotifyObservers({qubit});
960 mpo->ApplyRy(qubit, theta);
961 NotifyObservers({qubit});
966 mpo->ApplyRz(qubit, theta);
967 NotifyObservers({qubit});
970 void ApplyU(
Types::qubit_t qubit,
double theta,
double phi,
double lambda,
double gamma)
override
972 mpo->ApplyU(qubit, theta, phi, lambda, gamma);
973 NotifyObservers({qubit});
978 mpo->ApplyCX(ctrl_qubit, tgt_qubit);
979 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false, 2.);
980 NotifyObservers({tgt_qubit, ctrl_qubit});
985 mpo->ApplyCY(ctrl_qubit, tgt_qubit);
986 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false, 2.);
987 NotifyObservers({tgt_qubit, ctrl_qubit});
992 mpo->ApplyCZ(ctrl_qubit, tgt_qubit);
993 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false, 2.);
994 NotifyObservers({tgt_qubit, ctrl_qubit});
999 mpo->ApplyCP(ctrl_qubit, tgt_qubit, lambda);
1000 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false,
ControlledSchmidtRank(2. * std::abs(std::cos(lambda / 2.))));
1001 NotifyObservers({tgt_qubit, ctrl_qubit});
1006 mpo->ApplyCRx(ctrl_qubit, tgt_qubit, theta);
1007 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false,
ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
1008 NotifyObservers({tgt_qubit, ctrl_qubit});
1013 mpo->ApplyCRy(ctrl_qubit, tgt_qubit, theta);
1014 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false,
ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
1015 NotifyObservers({tgt_qubit, ctrl_qubit});
1020 mpo->ApplyCRz(ctrl_qubit, tgt_qubit, theta);
1021 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false,
ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
1022 NotifyObservers({tgt_qubit, ctrl_qubit});
1027 mpo->ApplyCH(ctrl_qubit, tgt_qubit);
1028 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false, 2.);
1029 NotifyObservers({tgt_qubit, ctrl_qubit});
1034 mpo->ApplyCSX(ctrl_qubit, tgt_qubit);
1036 NotifyObservers({tgt_qubit, ctrl_qubit});
1041 mpo->ApplyCSXDG(ctrl_qubit, tgt_qubit);
1043 NotifyObservers({tgt_qubit, ctrl_qubit});
1048 mpo->ApplySwap(qubit0, qubit1);
1049 RecordExecutedTwoQubitGate(qubit0, qubit1,
true, 4.);
1050 NotifyObservers({qubit1, qubit0});
1056 DecompositionScope scope(*
this);
1057 const size_t q1 = qubit0;
1058 const size_t q2 = qubit1;
1059 const size_t q3 = qubit2;
1060 mpo->ApplyCSX(
static_cast<int>(q2),
static_cast<int>(q3));
1061 RecordExecutedTwoQubitGate(qubit1, qubit2,
false, 2.);
1062 NotifyObservers({qubit1, qubit2});
1063 mpo->ApplyCX(
static_cast<int>(q1),
static_cast<int>(q2));
1064 RecordExecutedTwoQubitGate(qubit0, qubit1,
false, 2.);
1065 NotifyObservers({qubit0, qubit1});
1066 mpo->ApplyCSXDG(
static_cast<int>(q2),
static_cast<int>(q3));
1067 RecordExecutedTwoQubitGate(qubit1, qubit2,
false, 2.);
1068 NotifyObservers({qubit1, qubit2});
1069 mpo->ApplyCX(
static_cast<int>(q1),
static_cast<int>(q2));
1070 RecordExecutedTwoQubitGate(qubit0, qubit1,
false, 2.);
1071 NotifyObservers({qubit0, qubit1});
1072 mpo->ApplyCSX(
static_cast<int>(q1),
static_cast<int>(q3));
1073 RecordExecutedTwoQubitGate(qubit0, qubit2,
false, 2.);
1074 NotifyObservers({qubit0, qubit2});
1076 NotifyObservers({qubit2, qubit1, qubit0});
1082 DecompositionScope scope(*
this);
1083 const size_t q1 = ctrl_qubit;
1084 const size_t q2 = qubit0;
1085 const size_t q3 = qubit1;
1086 mpo->ApplyCX(
static_cast<int>(q3),
static_cast<int>(q2));
1087 RecordExecutedTwoQubitGate(qubit1, qubit0,
false, 2.);
1088 NotifyObservers({qubit1, qubit0});
1089 mpo->ApplyCSX(
static_cast<int>(q2),
static_cast<int>(q3));
1090 RecordExecutedTwoQubitGate(qubit0, qubit1,
false, 2.);
1091 NotifyObservers({qubit0, qubit1});
1092 mpo->ApplyCX(
static_cast<int>(q1),
static_cast<int>(q2));
1093 RecordExecutedTwoQubitGate(ctrl_qubit, qubit0,
false, 2.);
1094 NotifyObservers({ctrl_qubit, qubit0});
1095 mpo->ApplyP(
static_cast<int>(q3), M_PI);
1096 NotifyObservers({qubit1});
1097 mpo->ApplyP(
static_cast<int>(q2), -M_PI_2);
1098 NotifyObservers({qubit0});
1099 mpo->ApplyCSX(
static_cast<int>(q2),
static_cast<int>(q3));
1100 RecordExecutedTwoQubitGate(qubit0, qubit1,
false, 2.);
1101 NotifyObservers({qubit0, qubit1});
1102 mpo->ApplyCX(
static_cast<int>(q1),
static_cast<int>(q2));
1103 RecordExecutedTwoQubitGate(ctrl_qubit, qubit0,
false, 2.);
1104 NotifyObservers({ctrl_qubit, qubit0});
1105 mpo->ApplyP(
static_cast<int>(q3), M_PI);
1106 NotifyObservers({qubit1});
1107 mpo->ApplyCSX(
static_cast<int>(q1),
static_cast<int>(q3));
1108 RecordExecutedTwoQubitGate(ctrl_qubit, qubit1,
false, 2.);
1109 NotifyObservers({ctrl_qubit, qubit1});
1110 mpo->ApplyCX(
static_cast<int>(q3),
static_cast<int>(q2));
1111 RecordExecutedTwoQubitGate(qubit1, qubit0,
false, 2.);
1112 NotifyObservers({qubit1, qubit0});
1114 NotifyObservers({qubit1, qubit0, ctrl_qubit});
1119 mpo->ApplyCU(ctrl_qubit, tgt_qubit, theta, phi, lambda, gamma);
1120 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit,
false,
ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.) * std::cos((phi + lambda) / 2.))));
1121 NotifyObservers({tgt_qubit, ctrl_qubit});
1124 std::unique_ptr<ISimulator> Clone()
override
1126 auto cloned = std::make_unique<GpuMPOSimulator>();
1127 cloned->swapGatesRelabelQubits = swapGatesRelabelQubits;
1128 cloned->nrQubits = nrQubits;
1129 cloned->lookaheadDepth = lookaheadDepth;
1130 cloned->lookaheadDepthWithHeuristic = lookaheadDepthWithHeuristic;
1131 cloned->useOptimalMeetingPosition = useOptimalMeetingPosition;
1132 cloned->upcomingGates = upcomingGates;
1133 cloned->upcomingGateIndex = upcomingGateIndex;
1134 cloned->growthFactorGate = growthFactorGate;
1135 cloned->growthFactorSwap = growthFactorSwap;
1136 cloned->configuration = configuration;
1139 cloned->mpo = mpo->Clone();
1141 throw std::runtime_error(
"GpuSimulator::Clone: Failed to clone matrix product operator "
1143 cloned->gateCounterObserver = std::make_shared<GateCounterObserver>(cloned->upcomingGateIndex);
1144 cloned->RegisterObserver(cloned->gateCounterObserver);
1145 cloned->curMaxBondDim = curMaxBondDim;
1146 cloned->mpo->SetCallbackContext(
static_cast<GpuTensorChainSimulator *
>(cloned.get()));
1148 cloned->RefreshRoutingSettings();
1149 cloned->dummySim = dummySim ? dummySim->Clone() :
nullptr;
1150 cloned->dummyHistoryValid = dummyHistoryValid;
1151 cloned->savedDummyBondDims = savedDummyBondDims;
1152 cloned->RefreshRoutingCallback();
1153 if (configuration.IsSet(
"seed"))
1154 cloned->SetSeed(DeriveSeed(std::stoull(configuration.GetConfiguration(
"seed")), nextSeedStream++));
1159 std::vector<double> CurrentBondDimensions()
const override
1163 const auto dims = mpo->GetBondDimensions(nrQubits);
1164 return {dims.begin(), dims.end()};
1167 std::vector<long long> CurrentRoutingMap()
const override
1170 return mpo->GetQubitsMap();
1174 void RefreshRoutingCallback()
override
1176 const bool active = IsRoutingLookaheadEnabled() && !upcomingGates.empty();
1178 mpo->SetMeetingPositionCallback(active ? GetMeetingPositionCallback() :
nullptr);
1181 std::unique_ptr<GpuMPO> mpo;
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.
double EffectiveSchmidtRank(const Eigen::MatrixXcd &u)
double ControlledSchmidtRank(double traceAbs)
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
uint_fast64_t qubit_t
The type of a qubit.