2#if defined(INCLUDED_BY_FACTORY)
3#include "MPOSimulator.h"
5#define _USE_MATH_DEFINES
11class QCSimMPOSimulator :
public QCSimTensorChainSimulator
14 QCSimMPOSimulator() : QCSimTensorChainSimulator(true)
16 configuration.SetConfiguration(
"method",
"matrix_product_operator");
19 const char *MethodName()
const override
21 return "matrix_product_operator";
24 void Initialize()
override
26 InvalidateBoundedRouting(
true);
27 RefreshRoutingSettings();
28 InvalidateDummyHistory();
32 mpoSimulator = std::make_unique<QC::TensorNetworks::MPOSimulator>(nrQubits);
33 if (!useOptimalMeetingPosition)
34 mpoSimulator->SetUseOptimalMeetingPosition(
false);
39 for (
const auto &[key, value] : configuration.GetConfigMap())
41 Configure(key.c_str(), value.c_str());
43 RefreshRoutingCallback();
46 void InitializeToBasisState(
size_t num_qubits,
Types::qubit_t basisState)
override
51 nrQubits = num_qubits;
53 mpoSimulator->setToBasisState(
static_cast<size_t>(basisState));
56 void InitializeToBasisState(
size_t num_qubits,
const std::vector<bool> &basisState)
override
61 nrQubits = num_qubits;
63 mpoSimulator->setToBasisState(basisState);
66 void InitializeToMixtureOfBasisStates(
size_t num_qubits,
const std::vector<std::pair<Types::qubit_t, double>> &mixture)
override
71 nrQubits = num_qubits;
73 std::vector<std::pair<size_t, double>> converted;
74 converted.reserve(mixture.size());
75 for (
const auto &[basisState, weight] : mixture)
76 converted.emplace_back(
static_cast<size_t>(basisState), weight);
77 mpoSimulator->setToMixtureOfBasisStates(converted);
80 void InitializeToMixtureOfBasisStates(
size_t num_qubits,
const std::vector<std::pair<std::vector<bool>,
double>> &mixture)
override
85 nrQubits = num_qubits;
87 mpoSimulator->setToMixtureOfBasisStates(mixture);
94 mpoSimulator->Clear();
97 upcomingGateIndex = 0;
98 ResetDummySimulator();
101 void SetInitialQubitsMap(
const std::vector<long long int> &initialMap)
override
105 mpoSimulator->SetInitialQubitsMap(initialMap);
106 EnsureDummySimulator(initialMap.size());
107 dummySim->SetInitialQubitsMap(initialMap);
111 void SetUseOptimalMeetingPosition(
bool enable)
override
113 useOptimalMeetingPosition = enable;
114 RefreshRoutingSettings();
116 mpoSimulator->SetUseOptimalMeetingPosition(enable);
117 RefreshRoutingCallback();
120 void Configure(
const char *key,
const char *value)
override
122 if (std::string(
"method") == key)
124 ValidateMethod(value);
125 configuration.SetConfiguration(key, value);
128 if ((std::string(key) ==
"matrix_product_state_max_bond_dimension" || std::string(key) ==
"matrix_product_operator_max_bond_dimension"))
130 const auto limit = std::stoll(value);
132 throw std::invalid_argument(
"Negative MPO bond dimension");
136 mpoSimulator->dontLimitBondDimension();
138 mpoSimulator->setLimitBondDimension(limit);
140 const std::string limitValue(value);
141 configuration.SetConfiguration(
"matrix_product_state_max_bond_dimension", limitValue);
142 configuration.SetConfiguration(
"matrix_product_operator_max_bond_dimension", limitValue);
143 RefreshRoutingSettings();
144 RefreshRoutingCallback();
147 if (!configuration.WasApplied(key, value))
148 configuration.SetConfiguration(key, value);
149 RefreshRoutingSettings();
150 if (std::string(
"seed") == key)
152 const uint64_t seed = std::stoull(value);
153 SeedAuxiliaryRng(seed);
157 SeedBackend(mpoSimulator.get(), seed);
162 if (std::string(key) ==
"matrix_product_state_max_bond_dimension" || std::string(key) ==
"matrix_product_operator_max_bond_dimension")
164 mpoSimulator->setLimitBondDimension(configuration.GetConfigurationAsInt(key));
166 else if (std::string(key) ==
"matrix_product_state_truncation_threshold" || std::string(key) ==
"matrix_product_operator_truncation_threshold")
168 const double threshold = configuration.GetConfigurationAsDouble(key);
170 mpoSimulator->setLimitEntanglement(threshold);
172 else if (std::string(key) ==
"matrix_product_state_truncation_mode" || std::string(key) ==
"matrix_product_operator_truncation_mode")
174 if (std::string(value) ==
"relative_max")
175 mpoSimulator->setTruncationMode(QC::TensorNetworks::MPOSimulator::TruncationMode::RelativeToMax);
176 else if (std::string(value) ==
"discarded_weight")
177 mpoSimulator->setTruncationMode(QC::TensorNetworks::MPOSimulator::TruncationMode::DiscardedWeight);
179 else if (std::string(key) ==
"matrix_product_operator_kraus_completeness_check")
181 using Check = QC::TensorNetworks::MPOSimulator::KrausCompletenessCheck;
182 if (std::string(value) ==
"ignore")
183 mpoSimulator->setKrausCompletenessCheck(Check::Ignore);
184 else if (std::string(value) ==
"warn")
185 mpoSimulator->setKrausCompletenessCheck(Check::Warn);
186 else if (std::string(value) ==
"strict")
187 mpoSimulator->setKrausCompletenessCheck(Check::Strict);
189 else if (std::string(key) ==
"matrix_product_operator_restore_trace_after_truncation")
191 mpoSimulator->setRestoreTraceAfterTruncation(std::string(value) ==
"1" || std::string(value) ==
"true");
193 else if (std::string(key) ==
"matrix_product_operator_hermitize_after_truncation")
195 mpoSimulator->setHermitizeAfterTruncation(std::string(value) ==
"1" || std::string(value) ==
"true");
198 if (std::string(key) ==
"pauli_propagator_workers")
200 const auto workers = configuration.GetConfigurationAsUnsigned(key);
202 throw std::invalid_argument(
"pauli_propagator_workers exceeds 1024");
204 RefreshRoutingCallback();
211 if (std::string(key) ==
"precision")
213 if (std::string(key) ==
"use_double_precision")
217 if (std::string(key) ==
"matrix_product_operator_restore_trace_after_truncation")
218 return mpoSimulator->getRestoreTraceAfterTruncation() ?
"true" :
"false";
219 if (std::string(key) ==
"matrix_product_operator_hermitize_after_truncation")
220 return mpoSimulator->getHermitizeAfterTruncation() ?
"true" :
"false";
222 if (std::string(
"method") == key)
224 return QCSimTensorChainSimulator::GetConfiguration(key);
231 const size_t oldNrQubits = nrQubits;
232 nrQubits += num_qubits;
236 void Clear()
override
238 automaticRoutingDepth =
false;
239 InvalidateBoundedRouting(
true);
240 mpoSimulator =
nullptr;
241 ClearDummySimulator();
243 upcomingGateIndex = 0;
244 upcomingGates.clear();
249 InvalidateDummyHistory();
250 if (mpoSimulator && qubits.size() >
sizeof(
size_t) * 8)
251 throw std::invalid_argument(
"Use MeasureMany for more than 64 measured qubits");
252 if (qubits.size() >
sizeof(
size_t) * 8)
253 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
254 "number of bits in the size_t type, the outcome will be undefined"
260 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
261 const auto measured = mpoSimulator->MeasureQubits(qubitsSet);
264 if (measured.at(
static_cast<Eigen::Index
>(qubit)))
270 NotifyObservers(qubits);
276 InvalidateDummyHistory();
277 std::vector<bool> res(qubits.size(),
false);
280 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
281 const auto measured = mpoSimulator->MeasureQubits(qubitsSet);
282 for (
size_t q = 0; q < qubits.size(); ++q)
283 res[q] = measured.at(
static_cast<Eigen::Index
>(qubits[q]));
286 NotifyObservers(qubits);
292 InvalidateDummyHistory();
295 for (
size_t qubit : qubits)
296 mpoSimulator->ApplyReset(
static_cast<Eigen::Index
>(qubit));
299 NotifyObservers(qubits);
302 bool SupportsQuantumChannels()
const override
307 void ApplyQuantumChannel(
const Types::qubits_vector &targets,
const QuantumChannel &channel)
override
309 if (!SupportsQuantumChannels())
310 throw std::runtime_error(
"QCSim quantum channels require density_matrix or "
311 "matrix_product_operator simulation");
312 if (targets.size() != channel.GetNumberOfQubits())
313 throw std::invalid_argument(
"The number of channel targets does not match its Kraus operators");
314 if (targets.empty() || targets.size() > 2)
315 throw std::invalid_argument(
"QCSim supports only one- and two-qubit local channels");
316 std::unordered_set<Types::qubit_t> uniqueTargets;
319 if (target >= nrQubits)
320 throw std::invalid_argument(
"Quantum-channel qubit is out of range");
321 if (!uniqueTargets.insert(target).second)
322 throw std::invalid_argument(
"Quantum-channel target qubits must be distinct");
324 const auto &krausOperators = channel.GetKrausOperators();
327 InvalidateDummyHistory();
330 throw std::runtime_error(
"QCSim MPO state is not initialized");
331 if (targets.size() == 1)
332 mpoSimulator->ApplyKrausOperators(krausOperators,
static_cast<Eigen::Index
>(targets[0]));
335 mpoSimulator->ApplyKrausOperators(krausOperators,
static_cast<Eigen::Index
>(targets[0]),
static_cast<Eigen::Index
>(targets[1]));
338 InvalidateDummyHistory();
339 NotifyObservers(targets);
342 double ProbabilityBits(
const std::vector<bool> &bits)
override
345 return IState::ProbabilityBits(bits);
346 MPOValidation::Bits(bits, nrQubits);
347 return mpoSimulator->getBasisStateProbability(bits);
350 std::complex<double> DensityMatrixElementBits(
const std::vector<bool> &row,
const std::vector<bool> &col)
const override
353 return IState::DensityMatrixElementBits(row, col);
354 MPOValidation::Bits(row, nrQubits);
355 MPOValidation::Bits(col, nrQubits);
356 return mpoSimulator->getBasisStateMatrixElement(row, col);
359 Eigen::MatrixXcd GetDensityMatrix(
bool normalized =
true)
const override
362 return IState::GetDensityMatrix(normalized);
364 throw std::invalid_argument(
"MPO dense output supports at most 13 qubits");
365 return normalized ? mpoSimulator->getDensityMatrix() : mpoSimulator->getUnnormalizedDensityMatrix();
368 std::complex<double> ExpectationValueComplex(
const std::string &pauli,
bool normalized =
true)
const override
371 return IState::ExpectationValueComplex(pauli, normalized);
372 MPOValidation::Pauli(pauli, nrQubits);
373 return normalized ? mpoSimulator->ExpectationValue(pauli) : mpoSimulator->UnnormalizedExpectationValue(pauli);
376 std::vector<std::complex<double>> ExpectationValuesComplex(
const std::vector<std::string> &paulis,
bool normalized =
true)
const override
379 return IState::ExpectationValuesComplex(paulis, normalized);
380 for (
const auto &pauli : paulis)
381 MPOValidation::Pauli(pauli, nrQubits);
385 void ApplyOperator(
const Types::qubits_vector &qubits,
const Eigen::MatrixXcd &matrix,
bool normalize =
false)
override
388 return IState::ApplyOperator(qubits, matrix, normalize);
389 const auto selected = MPOValidation::Operator(qubits, matrix, nrQubits);
390 if (!normalize && qubits.size() == 2 && IsExactSwapMatrix(matrix))
392 const QC::Gates::AppliedGate<> op(matrix, selected[0], selected.size() == 2 ? selected[1] : 0);
395 InvalidateDummyHistory();
397 mpoSimulator->ApplyOperatorAndNormalize(op);
399 mpoSimulator->ApplyOperator(op);
400 InvalidateDummyHistory();
401 NotifyObservers(qubits);
407 return IState::MoveAtBeginningOfChain(qubits);
408 const auto selected = MPOValidation::Qubits(qubits, nrQubits);
409 const std::set<Eigen::Index> targets(selected.begin(), selected.end());
410 mpoSimulator->MoveAtBeginningOfChain(targets);
411 InvalidateDummyHistory();
414 std::complex<double> DensityMatrixTrace()
const override
417 return mpoSimulator->Trace();
418 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
419 "matrix_product_operator");
422 double DensityMatrixPurity()
const override
425 return mpoSimulator->Purity();
426 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
427 "matrix_product_operator");
430 std::complex<double> DensityMatrixTraceOfSquare()
const override
433 return mpoSimulator->TraceOfSquare();
434 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
435 "matrix_product_operator");
438 std::complex<double> DensityMatrixOverlap(
const IState &other)
const override
440 const auto *rhs =
dynamic_cast<const QCSimMPOSimulator *
>(&other);
442 throw std::invalid_argument(
"Density-matrix overlap requires matching QCSim backends");
443 if (mpoSimulator && rhs->mpoSimulator)
444 return mpoSimulator->HilbertSchmidtOverlap(*rhs->mpoSimulator);
445 throw std::invalid_argument(
"Density-matrix overlap requires two density matrices or two MPOs");
448 double DensityMatrixHermiticityResidual()
const override
451 return mpoSimulator->HermiticityResidual();
452 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
453 "matrix_product_operator");
456 bool IsDensityMatrixHermitian(
double eps = 1e-10)
const override
459 return mpoSimulator->IsHermitian(eps);
460 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or "
461 "matrix_product_operator");
467 return mpoSimulator->PartialTrace(std::vector<Eigen::Index>(qubits.begin(), qubits.end()));
468 throw std::runtime_error(
"Partial trace requires density_matrix or matrix_product_operator");
471 double FidelityWithStatevector(
const Eigen::VectorXcd &psi)
const override
474 return mpoSimulator->FidelityWithStatevector(psi);
475 throw std::runtime_error(
"Mixed-state fidelity requires density_matrix or "
476 "matrix_product_operator");
479 void RestoreDensityMatrixTrace()
override
482 throw std::runtime_error(
"Trace restoration is only available for QCSim MPO");
483 mpoSimulator->RestoreTrace();
486 void HermitizeDensityMatrix()
override
489 throw std::runtime_error(
"Hermitization is only available for QCSim MPO");
490 mpoSimulator->Hermitize();
495 InvalidateDummyHistory();
497 mpoSimulator->Trim();
499 throw std::runtime_error(
"Trim is only available for QCSim MPS and MPO");
502 void ReCanonicalize()
override
504 InvalidateDummyHistory();
506 mpoSimulator->ReCanonicalize();
508 throw std::runtime_error(
"Canonicalization is only available for QCSim MPS and MPO");
514 throw std::out_of_range(
"MPO basis state is out of range");
515 std::vector<bool> bits(nrQubits);
516 for (
size_t q = 0; q < std::min(nrQubits,
sizeof(
Types::qubit_t) * 8); ++q)
517 bits[q] = ((outcome >> q) & 1) != 0;
518 return ProbabilityBits(bits);
523 throw std::runtime_error(
"QCSimState::Amplitude: Amplitudes are not defined for the matrix "
524 "product operator simulator.");
529 const size_t nrBasisStates = CheckedBasisStateCountForQueries();
530 std::vector<double> result(nrBasisStates);
531 for (
size_t i = 0; i < nrBasisStates; ++i)
532 result[i] = mpoSimulator->getBasisStateProbability(i);
538 std::vector<double> result(qubits.size());
540 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
549 throw std::invalid_argument(
"Use SampleCountsMany for more than 64 measured qubits");
551 for (
auto q : qubits)
553 throw std::out_of_range(
"MPO sampled qubit is out of range");
554 if (qubits.empty() || shots == 0)
556 if (qubits.size() >
sizeof(
size_t) * 8)
557 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
558 "number of bits in the size_t type, the outcome will be undefined"
560 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
563 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
564 for (
size_t shot = 0; shot < shots; ++shot)
566 const auto measured = mpoSimulator->MeasureNoCollapse(qubitsSet);
568 for (
size_t i = 0; i < qubits.size(); ++i)
569 if (measured.at(
static_cast<Eigen::Index
>(qubits[i])))
575 NotifyObservers(qubits);
582 for (
auto q : qubits)
584 throw std::out_of_range(
"MPO sampled qubit is out of range");
585 if (qubits.empty() || shots == 0)
590 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
591 for (
size_t shot = 0; shot < shots; ++shot)
593 const auto measured = mpoSimulator->MeasureNoCollapse(qubitsSet);
594 std::vector<bool> packed(qubits.size(),
false);
595 for (
size_t i = 0; i < qubits.size(); ++i)
596 packed[i] = measured.at(
static_cast<Eigen::Index
>(qubits[i]));
601 NotifyObservers(qubits);
605 std::vector<double> ExpectationValues(
const std::vector<std::string> &paulis)
override
607 std::vector<double> result(paulis.size(), 1.);
608 std::vector<std::string> selected;
609 std::vector<size_t> indices;
611 for (
size_t i = 0; i < paulis.size(); ++i)
613 if (paulis[i].empty())
616 for (
size_t q = n; q < paulis[i].size(); ++q)
618 const auto p = toupper(
static_cast<unsigned char>(paulis[i][q]));
619 if (p !=
'I' && p !=
'Z')
630 std::string pauli = paulis[i];
631 pauli.resize(n,
'I');
632 for (
char &p : pauli)
634 p =
static_cast<char>(toupper(
static_cast<unsigned char>(p)));
636 indices.push_back(i);
637 selected.push_back(std::move(pauli));
639 if (selected.empty())
642 for (
size_t i = 0; i < indices.size(); ++i)
643 result[indices[i]] = values[i].real();
647 double ExpectationValue(
const std::string &pauliStringOrig)
override
649 if (pauliStringOrig.empty())
651 std::string pauliString = pauliStringOrig;
656 const auto pauliOp = toupper(pauliString[i]);
657 if (pauliOp !=
'I' && pauliOp !=
'Z')
664 return mpoSimulator->ExpectationValue(pauliString).real();
670 return SimulationType::kMatrixProductOperator;
675 mpoSimulator->SaveState();
681 mpoSimulator->RestoreState();
687 enableMultithreading = multithreading;
689 mpoSimulator->SetMultithreading(multithreading);
695 throw std::invalid_argument(
"Use MeasureNoCollapseMany for more than 64 qubits");
697 std::cerr <<
"Warning: The number of qubits to measure is larger than the "
698 "number of bits in the Types::qubit_t type, the outcome will be "
702 const auto measured = mpoSimulator->MeasureNoCollapse();
704 for (
size_t qubit = 0; qubit < nrQubits; ++qubit)
705 if (measured.at(
static_cast<Eigen::Index
>(qubit)))
706 result |= 1ULL << qubit;
711 std::vector<bool> MeasureNoCollapseMany()
override
713 const auto measured = mpoSimulator->MeasureNoCollapse();
714 std::vector<bool> res(nrQubits);
715 for (
size_t i = 0; i < nrQubits; ++i)
716 res[i] = measured.at(
static_cast<Eigen::Index
>(i));
722 const QC::Gates::AppliedGate<> agate(gate, qubit);
723 ApplyNativeGate(agate);
724 NotifyObservers({qubit});
729 if (IsExactSwapMatrix(gate))
731 const QC::Gates::AppliedGate<> agate(gate, qubit0, qubit1);
732 ApplyNativeGate(agate);
733 NotifyObservers({qubit0, qubit1});
738 pgate.SetPhaseShift(lambda);
739 ApplyNativeGate(pgate,
static_cast<unsigned int>(qubit));
740 NotifyObservers({qubit});
745 ApplyNativeGate(xgate,
static_cast<unsigned int>(qubit));
746 NotifyObservers({qubit});
751 ApplyNativeGate(ygate,
static_cast<unsigned int>(qubit));
752 NotifyObservers({qubit});
757 ApplyNativeGate(zgate,
static_cast<unsigned int>(qubit));
758 NotifyObservers({qubit});
763 ApplyNativeGate(h,
static_cast<unsigned int>(qubit));
764 NotifyObservers({qubit});
769 ApplyNativeGate(sgate,
static_cast<unsigned int>(qubit));
770 NotifyObservers({qubit});
775 ApplyNativeGate(sdggate,
static_cast<unsigned int>(qubit));
776 NotifyObservers({qubit});
781 ApplyNativeGate(tgate,
static_cast<unsigned int>(qubit));
782 NotifyObservers({qubit});
787 ApplyNativeGate(tdggate,
static_cast<unsigned int>(qubit));
788 NotifyObservers({qubit});
793 ApplyNativeGate(sxgate,
static_cast<unsigned int>(qubit));
794 NotifyObservers({qubit});
799 ApplyNativeGate(sxdaggate,
static_cast<unsigned int>(qubit));
800 NotifyObservers({qubit});
805 ApplyNativeGate(k,
static_cast<unsigned int>(qubit));
806 NotifyObservers({qubit});
811 rxgate.SetTheta(theta);
812 ApplyNativeGate(rxgate,
static_cast<unsigned int>(qubit));
813 NotifyObservers({qubit});
818 rygate.SetTheta(theta);
819 ApplyNativeGate(rygate,
static_cast<unsigned int>(qubit));
820 NotifyObservers({qubit});
825 rzgate.SetTheta(theta);
826 ApplyNativeGate(rzgate,
static_cast<unsigned int>(qubit));
827 NotifyObservers({qubit});
830 void ApplyU(
Types::qubit_t qubit,
double theta,
double phi,
double lambda,
double gamma)
override
832 ugate.SetParams(theta, phi, lambda, gamma);
833 ApplyNativeGate(ugate,
static_cast<unsigned int>(qubit));
834 NotifyObservers({qubit});
839 ApplyNativeGate(cxgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
840 NotifyObservers({tgt_qubit, ctrl_qubit});
845 ApplyNativeGate(cygate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
846 NotifyObservers({tgt_qubit, ctrl_qubit});
851 ApplyNativeGate(czgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
852 NotifyObservers({tgt_qubit, ctrl_qubit});
857 cpgate.SetPhaseShift(lambda);
858 ApplyNativeGate(cpgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
859 NotifyObservers({tgt_qubit, ctrl_qubit});
864 crxgate.SetTheta(theta);
865 ApplyNativeGate(crxgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
866 NotifyObservers({tgt_qubit, ctrl_qubit});
871 crygate.SetTheta(theta);
872 ApplyNativeGate(crygate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
873 NotifyObservers({tgt_qubit, ctrl_qubit});
878 crzgate.SetTheta(theta);
879 ApplyNativeGate(crzgate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
880 NotifyObservers({tgt_qubit, ctrl_qubit});
885 ApplyNativeGate(ch,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
886 NotifyObservers({tgt_qubit, ctrl_qubit});
891 ApplyNativeGate(csx,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
892 NotifyObservers({tgt_qubit, ctrl_qubit});
897 ApplyNativeGate(csxdag,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
898 NotifyObservers({tgt_qubit, ctrl_qubit});
903 ApplyNativeGate(swapgate,
static_cast<unsigned int>(qubit1),
static_cast<unsigned int>(qubit0));
904 NotifyObservers({qubit1, qubit0});
911 ApplyCSx(qubit1, qubit2);
913 ApplyCSxDAG(qubit1, qubit2);
915 ApplyCSx(qubit0, qubit2);
917 {qubit2, qubit1, qubit0});
925 ApplyCSx(qubit0, qubit1);
929 ApplyCSx(qubit0, qubit1);
932 ApplyCSx(ctrl_qubit, qubit1);
935 {qubit1, qubit0, ctrl_qubit});
940 cugate.SetParams(theta, phi, lambda, gamma);
941 ApplyNativeGate(cugate,
static_cast<unsigned int>(tgt_qubit),
static_cast<unsigned int>(ctrl_qubit));
942 NotifyObservers({tgt_qubit, ctrl_qubit});
945 std::unique_ptr<ISimulator> Clone()
override
947 auto cloned = std::make_unique<QCSimMPOSimulator>();
948 cloned->nrQubits = nrQubits;
949 cloned->enableMultithreading = enableMultithreading;
950 cloned->lookaheadDepth = lookaheadDepth;
951 cloned->automaticRoutingDepth = automaticRoutingDepth;
952 cloned->lookaheadDepthWithHeuristic = lookaheadDepthWithHeuristic;
953 cloned->useOptimalMeetingPosition = useOptimalMeetingPosition;
954 cloned->upcomingGates = upcomingGates;
955 cloned->upcomingGateIndex = upcomingGateIndex;
956 cloned->boundedRoutingOperations = boundedRoutingOperations;
957 cloned->boundedRoutingOperationsQubits = boundedRoutingOperationsQubits;
958 cloned->growthFactorGate = growthFactorGate;
959 cloned->growthFactorSwap = growthFactorSwap;
962 cloned->mpoSimulator = mpoSimulator->Clone();
963 cloned->curMaxBondDim = curMaxBondDim;
964 cloned->gateCounterObserver = std::make_shared<GateCounterObserver>(cloned->upcomingGateIndex);
965 cloned->RegisterObserver(cloned->gateCounterObserver);
966 cloned->mpoSimulator->SetMeetingPositionCallback(cloned->meetingPositionCallback);
969 for (
const auto &[key, value] : configuration.GetConfigMap())
970 cloned->Configure(key.c_str(), value.c_str());
971 cloned->RefreshRoutingSettings();
972 cloned->dummySim = dummySim ? dummySim->Clone() :
nullptr;
973 cloned->dummyHistoryValid = dummyHistoryValid;
974 cloned->savedDummyBondDims = savedDummyBondDims;
975 if (configuration.IsSet(
"seed"))
976 cloned->SetSeed(DeriveSeed(std::stoull(configuration.GetConfiguration(
"seed")), nextSeedStream++));
977 cloned->RefreshRoutingCallback();
982 std::vector<double> CurrentBondDimensions()
const override
986 const auto dims = mpoSimulator->getBondDimensions();
987 return {dims.begin(), dims.end()};
990 std::vector<long long> CurrentRoutingMap()
const override
997 void RefreshRoutingCallback()
override
999 RefreshBoundedRouting();
1000 const bool active = IsRoutingLookaheadEnabled() && !upcomingGates.empty();
1002 mpoSimulator->SetMeetingPositionCallback(active ? meetingPositionCallback :
nullptr);
1005 std::unique_ptr<QC::TensorNetworks::MPOSimulator> mpoSimulator;
1008 template <
class Function>
void ApplyDecomposedGate(Function &&apply,
const Types::qubits_vector &affectedQubits)
1011 DecompositionScope scope(*
this);
1012 std::forward<Function>(apply)();
1014 NotifyObservers(affectedQubits);
1017 template <
class Gate,
class...
Qubits>
void ApplyNativeGate(
const Gate &gate,
Qubits... qubits)
1019 if constexpr (
sizeof...(Qubits) <= 2)
1021 mpoSimulator->ApplyGate(gate,
static_cast<Eigen::Index
>(qubits)...);
1022 if constexpr (
sizeof...(Qubits) == 2)
1024 const std::array<Types::qubit_t, 2> qs{
static_cast<Types::qubit_t>(qubits)...};
1025 RecordExecutedTwoQubitGate(qs[0], qs[1], gate.isSwapGate(), NativeGateSchmidtRank(gate));
1027 else if constexpr (
sizeof...(Qubits) == 0)
1029 if (gate.getQubitsNumber() == 2)
1030 RecordExecutedTwoQubitGate(
static_cast<Types::qubit_t>(gate.getQubit1()),
static_cast<Types::qubit_t>(gate.getQubit2()), gate.isSwapGate(),
1031 NativeGateSchmidtRank(gate));
1036 throw std::runtime_error(
"ImmediateQCSimSimulator: The matrix product operator simulator "
1038 "only one- and two-qubit gate operators.");
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)
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 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)
std::vector< std::complex< double > > ComplexTensorExpectationBatch(const T &backend, const std::vector< std::string > &paulis, bool normalized)
std::vector< std::complex< double > > TensorExpectationBatch(T &backend, const std::vector< std::string > &paulis)
void InstallBondSummary(T &backend, Callback callback)
std::vector< long long > ReadRoutingMap(const T *simulator)
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.