18#ifdef INCLUDED_BY_FACTORY
31#include "DensityMatrix.h"
32#include "MPOSimulator.h"
33#include "MPSSimulator.h"
34#include "QubitRegister.h"
53template <
typename T,
typename =
void>
54struct HasSetSeed : std::false_type {};
56struct HasSetSeed<T, std::void_t<decltype(std::declval<T &>().SetSeed(
57 std::declval<uint64_t>()))>> : std::true_type {};
60void SeedBackend(T *backend, uint64_t seed) {
61 if constexpr (HasSetSeed<T>::value) backend->SetSeed(seed);
77class QCSimState :
public ISimulator {
79 QCSimState() : rng(std::random_device{}()), uniformZeroOne(0, 1) {
80 meetingPositionCallback = [
this](
const auto& bondDims)
81 -> QC::TensorNetworks::MPSSimulatorInterface::IndexType {
82 if (lookaheadDepth <= 0 ||
83 lookaheadDepth == std::numeric_limits<int>::max())
86 if (upcomingGates.empty() ||
88 static_cast<long long>(upcomingGates.size())) {
92 const size_t nQ = bondDims.size() + 1;
94 if (!dummySim || dummySim->getNrQubits() != nQ) {
95 dummySim = std::make_unique<Simulators::MPSDummySimulator>(nQ);
96 dummySim->SetMaxBondDimension(
97 configuration.GetConfigurationAsInt(MaxBondDimensionConfigKey()));
98 dummySim->setGrowthFactorGate(growthFactorGate);
99 dummySim->setGrowthFactorSwap(growthFactorSwap);
105 dummySim->setTotalSwappingCost(0);
108 std::vector<double> bondDimsD(bondDims.begin(), bondDims.end());
109 dummySim->SetCurrentBondDimensions(bondDimsD);
112#ifdef LOG_CALLBACK_INFO
113 std::cerr <<
"Bond dimensions before swapping and applying the gate:";
114 for (
size_t i = 0; i < bondDims.size(); ++i) {
115 std::cerr << bondDims[i] <<
" ";
117 std::cerr << std::endl;
120 const auto& op = upcomingGates[upcomingGateIndex];
121 const auto qbits = op->AffectedQubits();
123 if (qbits.size() != 2)
126#ifdef LOG_CALLBACK_INFO
127 const auto &qmap = dummySim->getQubitsMap();
129 std::cerr <<
"Applying 2-qubit gate on physical qubits " <<
130 qmap[qbits[0]] <<
" and "
134 std::cerr <<
"Finding best meeting position for upcoming gates starting at index "
135 << upcomingGateIndex <<
" with lookahead depth "
136 << lookaheadDepth <<
" and heuristic depth "
137 << lookaheadDepthWithHeuristic << std::endl;
139 std::cerr <<
"Affected qubits: ";
140 for (
const auto &q : qbits) std::cerr << q <<
" ";
141 std::cerr << std::endl;
144 double bestCost = std::numeric_limits<double>::infinity();
145 auto res = dummySim->FindBestMeetingPosition(
146 upcomingGates, upcomingGateIndex, lookaheadDepth,
147 lookaheadDepthWithHeuristic, 0, bestCost);
149#ifdef LOG_CALLBACK_INFO
150 std::cerr <<
"Swapping the two qubits on position: " << res <<
" and " << (res + 1) << std::endl;
153 dummySim->SwapQubitsToPosition(qbits[0], qbits[1], res);
154 dummySim->ApplyGate(op);
159#ifdef LOG_CALLBACK_INFO
160 const auto &expectedBondDims = dummySim->getCurrentBondDimensions();
161 std::cerr <<
"Expected bond dimensions after swapping and applying "
163 for (
size_t i = 0; i < expectedBondDims.size(); ++i) {
164 std::cerr << expectedBondDims[i] <<
" ";
166 std::cerr << std::endl;
168 std::cerr <<
"Best meeting position: " << res <<
" with estimated cost: " << bestCost << std::endl;
174 bondDimensionCallback = [
this](
const auto& bondDims) {
175 for (
int i = 0; i < static_cast<int>(bondDims.size()); ++i)
176 if (
static_cast<size_t>(bondDims[i]) > curMaxBondDim)
177 curMaxBondDim =
static_cast<size_t>(bondDims[i]);
188 void Initialize()
override {
190 if (simulationType == SimulationType::kMatrixProductState) {
192 std::make_unique<QC::TensorNetworks::MPSSimulator>(nrQubits);
195 if (!useOptimalMeetingPosition)
196 mpsSimulator->SetUseOptimalMeetingPosition(
false);
197 mpsSimulator->SetBondDimensionCallback(bondDimensionCallback);
200 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
202 std::make_unique<QC::TensorNetworks::MPOSimulator>(nrQubits);
203 if (!useOptimalMeetingPosition)
204 mpoSimulator->SetUseOptimalMeetingPosition(
false);
205 mpoSimulator->SetBondDimensionCallback(bondDimensionCallback);
207 }
else if (simulationType == SimulationType::kStabilizer)
209 std::make_unique<QC::Clifford::StabilizerSimulator>(nrQubits);
210 else if (simulationType == SimulationType::kTensorNetwork) {
212 std::make_unique<TensorNetworks::TensorNetwork>(nrQubits);
215 const auto tensorContractor =
216 std::make_shared<TensorNetworks::ForestContractor>();
217 tensorNetwork->SetContractor(tensorContractor);
218 }
else if (simulationType == SimulationType::kPauliPropagator) {
219 pp = std::make_unique<Simulators::QcsimPauliPropagator>();
220 pp->SetNrQubits(
static_cast<int>(nrQubits));
221 }
else if (simulationType == SimulationType::kPathIntegral) {
222 pathIntegralSimulator = std::make_unique<PathIntegralSimulator>();
223 pathIntegralSimulator->SetStartZeroState(nrQubits);
224 }
else if (simulationType == SimulationType::kDensityMatrix) {
225 densityMatrix = std::make_unique<QC::DensityMatrix<>>(nrQubits);
226 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
228 std::make_unique<Simulators::QCSimExtendedStabilizer>(nrQubits);
230 state = std::make_unique<QC::QubitRegister<>>(nrQubits);
235 for (
const auto& [key, value] : configuration.GetConfigMap())
236 if (key !=
"method") Configure(key.c_str(), value.c_str());
251 void InitializeState(
size_t num_qubits,
252 std::vector<std::complex<double>> &litudes)
override {
253 if (num_qubits == 0)
return;
255 nrQubits = num_qubits;
257 if (simulationType != SimulationType::kStatevector &&
258 simulationType != SimulationType::kDensityMatrix)
259 throw std::runtime_error(
260 "QCSimState::InitializeState: Invalid "
261 "simulation type for initializing the state.");
263 Eigen::VectorXcd amplitudesEigen(
264 Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(),
266 if (simulationType == SimulationType::kDensityMatrix)
267 densityMatrix->setFromStatevector(amplitudesEigen);
269 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
308 void InitializeState(
size_t num_qubits,
309 AER::Vector<std::complex<double>> &litudes)
override {
310 if (num_qubits == 0)
return;
312 nrQubits = num_qubits;
314 if (simulationType != SimulationType::kStatevector &&
315 simulationType != SimulationType::kDensityMatrix)
316 throw std::runtime_error(
317 "QCSimState::InitializeState: Invalid "
318 "simulation type for initializing the state.");
320 Eigen::VectorXcd amplitudesEigen(
321 Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(),
323 if (simulationType == SimulationType::kDensityMatrix)
324 densityMatrix->setFromStatevector(amplitudesEigen);
326 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
341 void InitializeState(
size_t num_qubits,
342 Eigen::VectorXcd &litudes)
override {
343 if (num_qubits == 0)
return;
345 nrQubits = num_qubits;
348 if (simulationType != SimulationType::kStatevector &&
349 simulationType != SimulationType::kDensityMatrix)
350 throw std::runtime_error(
351 "QCSimState::InitializeState: Invalid "
352 "simulation type for initializing the state.");
354 if (simulationType == SimulationType::kDensityMatrix)
355 densityMatrix->setFromStatevector(amplitudes);
357 state = std::make_unique<QC::QubitRegister<>>(nrQubits, amplitudes);
358 state->SetMultithreading(enableMultithreading);
373 void InitializeToBasisState(
size_t num_qubits,
375 if (num_qubits == 0)
return;
377 nrQubits = num_qubits;
380 if (simulationType == SimulationType::kDensityMatrix)
381 densityMatrix->setToBasisState(
static_cast<size_t>(basisState));
382 else if (simulationType == SimulationType::kMatrixProductOperator)
383 mpoSimulator->setToBasisState(
static_cast<size_t>(basisState));
384 else if (simulationType == SimulationType::kMatrixProductState)
385 mpsSimulator->setToBasisState(
static_cast<size_t>(basisState));
386 else if (simulationType == SimulationType::kStatevector)
387 state->setToBasisState(
static_cast<size_t>(basisState));
389 for (
size_t q = 0; q < num_qubits; ++q)
406 void InitializeToBasisState(
size_t num_qubits,
407 const std::vector<bool> &basisState)
override {
408 if (num_qubits == 0)
return;
410 nrQubits = num_qubits;
413 if (simulationType == SimulationType::kMatrixProductOperator)
414 mpoSimulator->setToBasisState(basisState);
415 else if (simulationType == SimulationType::kMatrixProductState)
416 mpsSimulator->setToBasisState(basisState);
418 for (
size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
433 void InitializeToMixtureOfBasisStates(
435 const std::vector<std::pair<Types::qubit_t, double>> &mixture)
437 if (num_qubits == 0)
return;
439 nrQubits = num_qubits;
442 if (simulationType != SimulationType::kDensityMatrix &&
443 simulationType != SimulationType::kMatrixProductOperator)
444 throw std::runtime_error(
445 "QCSimState::InitializeToMixtureOfBasisStates: Invalid simulation "
446 "type for initializing to a mixture of basis states.");
448 std::vector<std::pair<size_t, double>> converted;
449 converted.reserve(mixture.size());
450 for (
const auto &[basisState, weight] : mixture)
451 converted.emplace_back(
static_cast<size_t>(basisState), weight);
453 if (simulationType == SimulationType::kDensityMatrix)
454 densityMatrix->setToMixtureOfBasisStates(converted);
456 mpoSimulator->setToMixtureOfBasisStates(converted);
469 void InitializeToMixtureOfBasisStates(
471 const std::vector<std::pair<std::vector<bool>,
double>> &mixture)
473 if (num_qubits == 0)
return;
475 nrQubits = num_qubits;
478 if (simulationType != SimulationType::kMatrixProductOperator)
479 throw std::runtime_error(
480 "QCSimState::InitializeToMixtureOfBasisStates: Invalid simulation "
481 "type for initializing to a mixture of basis states.");
483 mpoSimulator->setToMixtureOfBasisStates(mixture);
492 void Reset()
override {
494 mpsSimulator->Clear();
496 }
else if (mpoSimulator) {
497 mpoSimulator->Clear();
499 }
else if (cliffordSimulator)
500 cliffordSimulator->Reset();
501 else if (tensorNetwork)
502 tensorNetwork->Clear();
506 pp->ClearOperations();
507 else if (pathIntegralSimulator) {
508 pathIntegralSimulator->Reset();
509 pathIntegralSimulator->SetStartZeroState(nrQubits);
510 }
else if (densityMatrix)
511 densityMatrix->Reset();
512 else if (extendedStabilizer)
513 extendedStabilizer->Reset(nrQubits);
515 upcomingGateIndex = 0;
516 ResetDummySimulator();
526 bool SupportsMPSSwapOptimization()
const override {
return true; }
536 void SetInitialQubitsMap(
537 const std::vector<long long int> &initialMap)
override {
538 if (mpsSimulator || mpoSimulator) {
539 if (mpsSimulator) mpsSimulator->SetInitialQubitsMap(initialMap);
540 else mpoSimulator->SetInitialQubitsMap(initialMap);
542 if (!dummySim || dummySim->getNrQubits() != initialMap.size()) {
544 std::make_unique<Simulators::MPSDummySimulator>(initialMap.size());
545 dummySim->SetMaxBondDimension(
546 configuration.GetConfigurationAsInt(MaxBondDimensionConfigKey()));
548 dummySim->setGrowthFactorGate(growthFactorGate);
549 dummySim->setGrowthFactorSwap(growthFactorSwap);
550 dummySim->SetInitialQubitsMap(initialMap);
554 void SetUseOptimalMeetingPosition(
bool enable)
override {
555 useOptimalMeetingPosition = enable;
556 if (mpsSimulator) mpsSimulator->SetUseOptimalMeetingPosition(enable);
557 else if (mpoSimulator)
558 mpoSimulator->SetUseOptimalMeetingPosition(enable);
561 void SetLookaheadDepth(
int depth)
override {
562 lookaheadDepth = depth;
563 if (depth > 0 && !useOptimalMeetingPosition) {
564 if (mpsSimulator) mpsSimulator->SetUseOptimalMeetingPosition(
true);
565 else if (mpoSimulator)
566 mpoSimulator->SetUseOptimalMeetingPosition(
true);
570 void SetLookaheadDepthWithHeuristic(
int depth)
override {
571 lookaheadDepthWithHeuristic = depth;
572 if (lookaheadDepth < depth) SetLookaheadDepth(depth);
575 void SetUpcomingGates(
578 upcomingGates = gates;
579 upcomingGateIndex = 0;
581 if (!mpsSimulator && !mpoSimulator)
return;
586 gateCounterObserver =
587 std::make_shared<GateCounterObserver>(upcomingGateIndex);
588 RegisterObserver(gateCounterObserver);
595 mpsSimulator->SetMeetingPositionCallback(meetingPositionCallback);
597 mpoSimulator->SetMeetingPositionCallback(meetingPositionCallback);
608 long long int GetGatesCounter()
const override {
return upcomingGateIndex; }
619 void SetGatesCounter(
long long int counter)
override {
620 upcomingGateIndex = counter;
631 void IncrementGatesCounter()
override { ++upcomingGateIndex; }
633 double getGrowthFactorSwap()
const override {
return growthFactorSwap; }
634 double getGrowthFactorGate()
const override {
return growthFactorGate; }
636 void setGrowthFactorSwap(
double factor)
override {
637 growthFactorSwap = factor;
638 if (dummySim) dummySim->setGrowthFactorSwap(factor);
641 void setGrowthFactorGate(
double factor)
override {
642 growthFactorGate = factor;
643 if (dummySim) dummySim->setGrowthFactorGate(factor);
654 void Configure(
const char *key,
const char *value)
override {
655 if (std::string(
"method") == key) {
656 if (std::string(
"statevector") == value)
657 simulationType = SimulationType::kStatevector;
658 else if (std::string(
"matrix_product_state") == value)
659 simulationType = SimulationType::kMatrixProductState;
660 else if (std::string(
"matrix_product_operator") == value)
661 simulationType = SimulationType::kMatrixProductOperator;
662 else if (std::string(
"stabilizer") == value)
663 simulationType = SimulationType::kStabilizer;
664 else if (std::string(
"tensor_network") == value)
665 simulationType = SimulationType::kTensorNetwork;
666 else if (std::string(
"pauli_propagator") == value)
667 simulationType = SimulationType::kPauliPropagator;
668 else if (std::string(
"path_integral") == value)
669 simulationType = SimulationType::kPathIntegral;
670 else if (std::string(
"density_matrix") == value)
671 simulationType = SimulationType::kDensityMatrix;
672 else if (std::string(
"extended_stabilizer") == value)
673 simulationType = SimulationType::kExtendedStabilizer;
676 if (!configuration.WasApplied(key, value))
677 configuration.SetConfiguration(key, value);
679 if (std::string(
"seed") == key) {
680 const uint64_t seed = std::stoull(value);
683 if (state) SeedBackend(state.get(), seed);
684 if (mpsSimulator) SeedBackend(mpsSimulator.get(), seed);
685 if (mpoSimulator) SeedBackend(mpoSimulator.get(), seed);
686 if (cliffordSimulator) SeedBackend(cliffordSimulator.get(), seed);
687 if (tensorNetwork) tensorNetwork->SetSeed(seed);
688 if (pp) SeedBackend(pp.get(), seed);
689 if (pathIntegralSimulator) pathIntegralSimulator->SetSeed(seed);
690 if (densityMatrix) SeedBackend(densityMatrix.get(), seed);
691 if (extendedStabilizer)
692 extendedStabilizer->SetRandomSeed(
693 static_cast<std::mt19937::result_type
>(seed));
698 if (std::string(key) ==
"matrix_product_state_max_bond_dimension") {
699 mpsSimulator->setLimitBondDimension(configuration.GetConfigurationAsInt(key));
700 }
else if (std::string(key) ==
"matrix_product_state_truncation_threshold") {
701 const double threshold = configuration.GetConfigurationAsDouble(key);
702 if (threshold > 0.) mpsSimulator->setLimitEntanglement(threshold);
703 }
else if (std::string(key) ==
"matrix_product_state_truncation_mode") {
706 if (std::string(value) ==
"relative_max")
707 mpsSimulator->setTruncationMode(
708 QC::TensorNetworks::MPSSimulator::TruncationMode::RelativeToMax);
709 else if (std::string(value) ==
"discarded_weight")
710 mpsSimulator->setTruncationMode(
711 QC::TensorNetworks::MPSSimulator::TruncationMode::DiscardedWeight);
716 if (std::string(key) ==
"matrix_product_state_max_bond_dimension" ||
717 std::string(key) ==
"matrix_product_operator_max_bond_dimension") {
718 mpoSimulator->setLimitBondDimension(
719 configuration.GetConfigurationAsInt(key));
721 std::string(key) ==
"matrix_product_state_truncation_threshold" ||
722 std::string(key) ==
"matrix_product_operator_truncation_threshold") {
723 const double threshold = configuration.GetConfigurationAsDouble(key);
724 if (threshold > 0.) mpoSimulator->setLimitEntanglement(threshold);
726 std::string(key) ==
"matrix_product_state_truncation_mode" ||
727 std::string(key) ==
"matrix_product_operator_truncation_mode") {
729 if (std::string(value) ==
"relative_max")
730 mpoSimulator->setTruncationMode(
731 QC::TensorNetworks::MPOSimulator::TruncationMode::RelativeToMax);
732 else if (std::string(value) ==
"discarded_weight")
733 mpoSimulator->setTruncationMode(
734 QC::TensorNetworks::MPOSimulator::TruncationMode::DiscardedWeight);
735 }
else if (std::string(key) ==
736 "matrix_product_operator_kraus_completeness_check") {
737 using Check = QC::TensorNetworks::MPOSimulator::KrausCompletenessCheck;
738 if (std::string(value) ==
"ignore")
739 mpoSimulator->setKrausCompletenessCheck(Check::Ignore);
740 else if (std::string(value) ==
"warn")
741 mpoSimulator->setKrausCompletenessCheck(Check::Warn);
742 else if (std::string(value) ==
"strict")
743 mpoSimulator->setKrausCompletenessCheck(Check::Strict);
744 }
else if (std::string(key) ==
745 "matrix_product_operator_restore_trace_after_truncation") {
746 mpoSimulator->setRestoreTraceAfterTruncation(
747 std::string(value) ==
"1" || std::string(value) ==
"true");
748 }
else if (std::string(key) ==
749 "matrix_product_operator_hermitize_after_truncation") {
750 mpoSimulator->setHermitizeAfterTruncation(
751 std::string(value) ==
"1" || std::string(value) ==
"true");
756 if (std::string(key) ==
"pauli_propagator_coefficient_threshold") {
757 pp->SetCoefficientThreshold(configuration.GetConfigurationAsDouble(key));
758 }
else if (std::string(key) ==
"pauli_propagator_pauli_weight_threshold") {
759 pp->SetPauliWeightThreshold(
760 configuration.GetConfigurationAsUnsigned(key));
761 }
else if (std::string(key) ==
"pauli_propagator_steps_between_trims") {
762 pp->SetStepsBetweenTrims(configuration.GetConfigurationAsInt(key));
763 }
else if (std::string(key) ==
764 "pauli_propagator_num_gates_between_deduplications") {
765 pp->SetStepsBetweenDeduplication(
766 configuration.GetConfigurationAsInt(key));
770 if (pathIntegralSimulator) {
771 if (std::string(key) ==
"path_integral_threshold") {
772 pathIntegralSimulator->SetTrimValue(configuration.GetConfigurationAsDouble(key));
785 if (std::string(
"method") == key) {
786 switch (simulationType) {
787 case SimulationType::kStatevector:
788 return "statevector";
789 case SimulationType::kMatrixProductState:
790 return "matrix_product_state";
791 case SimulationType::kMatrixProductOperator:
792 return "matrix_product_operator";
793 case SimulationType::kStabilizer:
795 case SimulationType::kTensorNetwork:
796 return "tensor_network";
797 case SimulationType::kPauliPropagator:
798 return "pauli_propagator";
799 case SimulationType::kPathIntegral:
800 return "path_integral";
801 case SimulationType::kDensityMatrix:
802 return "density_matrix";
803 case SimulationType::kExtendedStabilizer:
804 return "extended_stabilizer";
810 return configuration.GetConfiguration(key);
821 if ((simulationType == SimulationType::kStatevector && state) ||
822 (simulationType == SimulationType::kMatrixProductState &&
824 (simulationType == SimulationType::kMatrixProductOperator &&
826 (simulationType == SimulationType::kStabilizer && cliffordSimulator) ||
827 (simulationType == SimulationType::kTensorNetwork && tensorNetwork) ||
828 (simulationType == SimulationType::kDensityMatrix && densityMatrix) ||
829 (simulationType == SimulationType::kExtendedStabilizer &&
833 const size_t oldNrQubits = nrQubits;
834 nrQubits += num_qubits;
835 if (simulationType == SimulationType::kPauliPropagator)
836 if (pp) pp->SetNrQubits(
static_cast<int>(nrQubits));
856 void Clear()
override {
858 mpsSimulator =
nullptr;
859 mpoSimulator =
nullptr;
860 cliffordSimulator =
nullptr;
861 tensorNetwork =
nullptr;
863 pathIntegralSimulator =
nullptr;
864 densityMatrix =
nullptr;
865 extendedStabilizer =
nullptr;
868 upcomingGateIndex = 0;
869 upcomingGates.clear();
886 if (qubits.size() >
sizeof(
size_t) * 8)
888 <<
"Warning: The number of qubits to measure is larger than the "
889 "number of bits in the size_t type, the outcome will be undefined"
896 if (simulationType == SimulationType::kStatevector) {
897 for (
size_t qubit : qubits) {
898 if (state->MeasureQubit(
static_cast<unsigned int>(qubit))) res |= mask;
901 }
else if (simulationType == SimulationType::kDensityMatrix) {
902 for (
size_t qubit : qubits) {
903 if (densityMatrix->MeasureQubit(qubit)) res |= mask;
906 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
907 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
908 const auto measured = mpoSimulator->MeasureQubits(qubitsSet);
910 if (measured.at(
static_cast<Eigen::Index
>(qubit))) res |= mask;
913 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
914 for (
size_t qubit : qubits) {
915 if (extendedStabilizer->Measure(qubit)) res |= mask;
918 }
else if (simulationType == SimulationType::kStabilizer) {
919 for (
size_t qubit : qubits) {
920 if (cliffordSimulator->MeasureQubit(
static_cast<unsigned int>(qubit)))
924 }
else if (simulationType == SimulationType::kTensorNetwork) {
925 for (
size_t qubit : qubits) {
926 if (tensorNetwork->Measure(
static_cast<unsigned int>(qubit)))
930 }
else if (simulationType == SimulationType::kPauliPropagator) {
931 std::vector<int> qubitsInt;
932 qubitsInt.reserve(qubits.size());
933 for (
const auto q : qubits)
934 qubitsInt.push_back(
static_cast<int>(q));
935 const auto res = pp->Measure(qubitsInt);
937 for (
size_t i = 0; i < res.size(); ++i) {
938 if (res[i]) result |= mask;
942 }
else if (simulationType == SimulationType::kPathIntegral) {
943 for (
size_t qubit : qubits) {
944 if (pathIntegralSimulator->MeasureQubit(qubit)) res |= mask;
956 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
957 auto measured = mpsSimulator->MeasureQubits(qubitsSet);
959 if (measured[qubit]) res |= mask;
965 NotifyObservers(qubits);
977 std::vector<bool> res(qubits.size(),
false);
980 if (simulationType == SimulationType::kStatevector) {
981 for (
size_t q = 0; q < qubits.size(); ++q)
982 if (state->MeasureQubit(
static_cast<unsigned int>(qubits[q])))
984 }
else if (simulationType == SimulationType::kDensityMatrix) {
985 for (
size_t q = 0; q < qubits.size(); ++q)
986 if (densityMatrix->MeasureQubit(qubits[q])) res[q] =
true;
987 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
988 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
989 const auto measured = mpoSimulator->MeasureQubits(qubitsSet);
990 for (
size_t q = 0; q < qubits.size(); ++q)
991 res[q] = measured.at(
static_cast<Eigen::Index
>(qubits[q]));
992 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
993 for (
size_t q = 0; q < qubits.size(); ++q)
994 if (extendedStabilizer->Measure(qubits[q])) res[q] =
true;
995 }
else if (simulationType == SimulationType::kStabilizer) {
996 for (
size_t q = 0; q < qubits.size(); ++q)
997 if (cliffordSimulator->MeasureQubit(
998 static_cast<unsigned int>(qubits[q])))
1000 }
else if (simulationType == SimulationType::kTensorNetwork) {
1001 for (
size_t q = 0; q < qubits.size(); ++q)
1002 if (tensorNetwork->Measure(
static_cast<unsigned int>(qubits[q])))
1004 }
else if (simulationType == SimulationType::kPauliPropagator) {
1005 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1006 res = pp->Measure(qubitsInt);
1007 }
else if (simulationType == SimulationType::kPathIntegral) {
1008 for (
size_t q = 0; q < qubits.size(); ++q)
1009 if (pathIntegralSimulator->MeasureQubit(qubits[q])) res[q] =
true;
1011 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
1012 auto measured = mpsSimulator->MeasureQubits(qubitsSet);
1013 for (
size_t q = 0; q < qubits.size(); ++q)
1014 if (measured[qubits[q]]) res[q] =
true;
1017 NotifyObservers(qubits);
1029 QC::Gates::PauliXGate xGate;
1032 if (simulationType == SimulationType::kStatevector) {
1033 for (
size_t qubit : qubits)
1034 if (state->MeasureQubit(
static_cast<unsigned int>(qubit)))
1035 state->ApplyGate(xGate,
static_cast<unsigned int>(qubit));
1036 }
else if (simulationType == SimulationType::kDensityMatrix) {
1037 for (
size_t qubit : qubits) densityMatrix->ApplyReset(qubit);
1038 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
1039 for (
size_t qubit : qubits)
1040 mpoSimulator->ApplyReset(
static_cast<Eigen::Index
>(qubit));
1041 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
1042 for (
size_t qubit : qubits)
1043 if (extendedStabilizer->Measure(qubit))
1044 extendedStabilizer->ApplyX(qubit);
1045 }
else if (simulationType == SimulationType::kStabilizer) {
1046 for (
size_t qubit : qubits)
1047 if (cliffordSimulator->MeasureQubit(
static_cast<unsigned int>(qubit)))
1048 cliffordSimulator->ApplyX(
static_cast<unsigned int>(qubit));
1049 }
else if (simulationType == SimulationType::kTensorNetwork) {
1050 for (
size_t qubit : qubits)
1051 if (tensorNetwork->Measure(
static_cast<unsigned int>(qubit)))
1052 tensorNetwork->AddGate(xGate,
static_cast<unsigned int>(qubit));
1053 }
else if (simulationType == SimulationType::kPauliPropagator) {
1054 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1055 const auto res = pp->Measure(qubitsInt);
1056 for (
size_t i = 0; i < res.size(); ++i) {
1057 if (res[i]) pp->ApplyX(qubitsInt[i]);
1059 }
else if (simulationType == SimulationType::kPathIntegral) {
1060 for (
size_t qubit : qubits)
1061 if (pathIntegralSimulator->MeasureQubit(qubit)) {
1062 QC::Gates::AppliedGate<> gate(xGate.getRawOperatorMatrix(), qubit);
1063 pathIntegralSimulator->PropagateStep(
1064 gate, pathIntegralSimulator->Amplitudes());
1067 for (
size_t qubit : qubits)
1068 if (mpsSimulator->MeasureQubit(
static_cast<unsigned int>(qubit)))
1069 mpsSimulator->ApplyGate(xGate,
static_cast<unsigned int>(qubit));
1073 NotifyObservers(qubits);
1076 bool SupportsQuantumChannels()
const override {
1077 return simulationType == SimulationType::kDensityMatrix ||
1078 simulationType == SimulationType::kMatrixProductOperator;
1091 const QuantumChannel &channel)
override {
1092 if (!SupportsQuantumChannels())
1093 throw std::runtime_error(
1094 "QCSim quantum channels require density_matrix or "
1095 "matrix_product_operator simulation");
1096 if (targets.size() != channel.GetNumberOfQubits())
1097 throw std::invalid_argument(
1098 "The number of channel targets does not match its Kraus operators");
1099 if (targets.empty() || targets.size() > 2)
1100 throw std::invalid_argument(
1101 "QCSim supports only one- and two-qubit local channels");
1103 std::unordered_set<Types::qubit_t> uniqueTargets;
1105 if (target >= nrQubits)
1106 throw std::invalid_argument(
"Quantum-channel qubit is out of range");
1107 if (!uniqueTargets.insert(target).second)
1108 throw std::invalid_argument(
1109 "Quantum-channel target qubits must be distinct");
1112 const auto &krausOperators = channel.GetKrausOperators();
1113 if (simulationType == SimulationType::kDensityMatrix) {
1115 throw std::runtime_error(
1116 "QCSim density-matrix state is not initialized");
1117 if (targets.size() == 1)
1118 densityMatrix->ApplyChannel(krausOperators, targets[0]);
1120 densityMatrix->ApplyChannel(krausOperators, targets[0], targets[1]);
1123 throw std::runtime_error(
"QCSim MPO state is not initialized");
1124 if (targets.size() == 1)
1125 mpoSimulator->ApplyKrausOperators(
1126 krausOperators,
static_cast<Eigen::Index
>(targets[0]));
1128 mpoSimulator->ApplyKrausOperators(
1129 krausOperators,
static_cast<Eigen::Index
>(targets[0]),
1130 static_cast<Eigen::Index
>(targets[1]));
1133 NotifyObservers(targets);
1136 std::complex<double> DensityMatrixTrace()
const override {
1137 if (densityMatrix)
return densityMatrix->Trace();
1138 if (mpoSimulator)
return mpoSimulator->Trace();
1139 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or matrix_product_operator");
1141 double DensityMatrixPurity()
const override {
1142 if (densityMatrix)
return densityMatrix->Purity();
1143 if (mpoSimulator)
return mpoSimulator->Purity();
1144 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or matrix_product_operator");
1146 std::complex<double> DensityMatrixTraceOfSquare()
const override {
1147 if (densityMatrix) {
1148 const auto &rho = densityMatrix->getDensityMatrix();
1149 return (rho * rho).trace();
1151 if (mpoSimulator)
return mpoSimulator->TraceOfSquare();
1152 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or matrix_product_operator");
1154 std::complex<double> DensityMatrixOverlap(
const IState &other)
const override {
1155 const auto *rhs =
dynamic_cast<const QCSimState *
>(&other);
1156 if (!rhs)
throw std::invalid_argument(
"Density-matrix overlap requires matching QCSim backends");
1157 if (densityMatrix && rhs->densityMatrix)
1158 return densityMatrix->HilbertSchmidtOverlap(*rhs->densityMatrix);
1159 if (mpoSimulator && rhs->mpoSimulator)
1160 return mpoSimulator->HilbertSchmidtOverlap(*rhs->mpoSimulator);
1161 throw std::invalid_argument(
"Density-matrix overlap requires two density matrices or two MPOs");
1163 double DensityMatrixHermiticityResidual()
const override {
1164 if (densityMatrix)
return (densityMatrix->getDensityMatrix() - densityMatrix->getDensityMatrix().adjoint()).norm();
1165 if (mpoSimulator)
return mpoSimulator->HermiticityResidual();
1166 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or matrix_product_operator");
1168 bool IsDensityMatrixHermitian(
double eps = 1e-10)
const override {
1169 if (densityMatrix)
return densityMatrix->IsHermitian(eps);
1170 if (mpoSimulator)
return mpoSimulator->IsHermitian(eps);
1171 throw std::runtime_error(
"Mixed-state diagnostics require density_matrix or matrix_product_operator");
1174 if (densityMatrix)
return densityMatrix->PartialTrace(std::vector<size_t>(qubits.begin(), qubits.end()));
1175 if (mpoSimulator)
return mpoSimulator->PartialTrace(std::vector<Eigen::Index>(qubits.begin(), qubits.end()));
1176 throw std::runtime_error(
"Partial trace requires density_matrix or matrix_product_operator");
1178 double FidelityWithStatevector(
const Eigen::VectorXcd &psi)
const override {
1179 if (densityMatrix)
return densityMatrix->FidelityWithStatevector(psi);
1180 if (mpoSimulator)
return mpoSimulator->FidelityWithStatevector(psi);
1181 throw std::runtime_error(
"Mixed-state fidelity requires density_matrix or matrix_product_operator");
1183 void RestoreDensityMatrixTrace()
override {
1184 if (!mpoSimulator)
throw std::runtime_error(
"Trace restoration is only available for QCSim MPO");
1185 mpoSimulator->RestoreTrace();
1187 void HermitizeDensityMatrix()
override {
1188 if (!mpoSimulator)
throw std::runtime_error(
"Hermitization is only available for QCSim MPO");
1189 mpoSimulator->Hermitize();
1191 void TrimMatrixProductOperator()
override {
1192 if (!mpoSimulator)
throw std::runtime_error(
"QCSim MPO is not initialized");
1193 mpoSimulator->Trim();
1195 void ReCanonicalizeMatrixProductOperator()
override {
1196 if (!mpoSimulator)
throw std::runtime_error(
"QCSim MPO is not initialized");
1197 mpoSimulator->ReCanonicalize();
1212 if (simulationType == SimulationType::kMatrixProductState)
1213 return mpsSimulator->getBasisStateProbability(
1214 static_cast<unsigned int>(outcome));
1215 else if (simulationType == SimulationType::kStabilizer)
1216 return cliffordSimulator->getBasisStateProbability(
1217 static_cast<unsigned int>(outcome));
1218 else if (simulationType == SimulationType::kTensorNetwork)
1219 return tensorNetwork->getBasisStateProbability(outcome);
1220 else if (simulationType == SimulationType::kPauliPropagator)
1221 return pp->Probability(outcome);
1222 else if (simulationType == SimulationType::kPathIntegral)
1223 return pathIntegralSimulator->Probability(outcome);
1224 else if (simulationType == SimulationType::kDensityMatrix)
1225 return densityMatrix->getBasisStateProbability(outcome);
1226 else if (simulationType == SimulationType::kMatrixProductOperator)
1227 return mpoSimulator->getBasisStateProbability(outcome);
1228 else if (simulationType == SimulationType::kExtendedStabilizer)
1229 return ExtendedStabilizerBasisProbability(outcome);
1231 return state->getBasisStateProbability(
static_cast<unsigned int>(outcome));
1245 if (simulationType == SimulationType::kMatrixProductState)
1246 return mpsSimulator->getBasisStateAmplitude(
1247 static_cast<unsigned int>(outcome));
1248 else if (simulationType == SimulationType::kPathIntegral)
1249 return pathIntegralSimulator->AmplitudeForOutcome(outcome);
1250 else if (simulationType == SimulationType::kStabilizer)
1251 throw std::runtime_error(
1252 "QCSimState::Amplitude: Invalid simulation type for obtaining the "
1253 "amplitude of the specified outcome.");
1254 else if (simulationType == SimulationType::kTensorNetwork)
1255 throw std::runtime_error(
1256 "QCSimState::Amplitude: Not supported for the "
1257 "tensor network simulator.");
1258 else if (simulationType == SimulationType::kPauliPropagator)
1259 throw std::runtime_error(
1260 "QCSimState::Amplitude: Invalid simulation type for obtaining the "
1261 "amplitude of the specified outcome.");
1262 else if (simulationType == SimulationType::kDensityMatrix)
1263 throw std::runtime_error(
1264 "QCSimState::Amplitude: Amplitudes are not defined for the density "
1265 "matrix simulator.");
1266 else if (simulationType == SimulationType::kMatrixProductOperator)
1267 throw std::runtime_error(
1268 "QCSimState::Amplitude: Amplitudes are not defined for the matrix "
1269 "product operator simulator.");
1270 else if (simulationType == SimulationType::kExtendedStabilizer)
1271 throw std::runtime_error(
1272 "QCSimState::Amplitude: Amplitudes are not exposed by the extended "
1273 "stabilizer simulator.");
1275 return state->getBasisStateAmplitude(
static_cast<unsigned int>(outcome));
1291 std::complex<double> ProjectOnZero()
override {
1292 if (simulationType == SimulationType::kMatrixProductState)
1293 return mpsSimulator->ProjectOnZero();
1310 if (simulationType == SimulationType::kTensorNetwork)
1311 throw std::runtime_error(
1312 "QCSimState::AllProbabilities: Invalid "
1313 "simulation type for obtaining probabilities.");
1314 else if (simulationType == SimulationType::kStabilizer)
1315 return cliffordSimulator->AllProbabilities();
1316 else if (simulationType == SimulationType::kPauliPropagator) {
1318 std::vector<double> result(nrBasisStates);
1319 for (
size_t i = 0; i < nrBasisStates; ++i) result[i] = pp->Probability(i);
1321 }
else if (simulationType == SimulationType::kPathIntegral) {
1323 std::vector<double> result(nrBasisStates);
1324 for (
size_t i = 0; i < nrBasisStates; ++i)
1325 result[i] = pathIntegralSimulator->Probability(i);
1327 }
else if (simulationType == SimulationType::kDensityMatrix) {
1328 const size_t nrBasisStates = densityMatrix->getNrBasisStates();
1329 std::vector<double> result(nrBasisStates);
1330 for (
size_t i = 0; i < nrBasisStates; ++i)
1331 result[i] = densityMatrix->getBasisStateProbability(i);
1333 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
1334 const size_t nrBasisStates = CheckedBasisStateCountForQueries();
1335 std::vector<double> result(nrBasisStates);
1336 for (
size_t i = 0; i < nrBasisStates; ++i)
1337 result[i] = mpoSimulator->getBasisStateProbability(i);
1339 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
1340 const size_t nrBasisStates = CheckedBasisStateCountForQueries();
1341 std::vector<double> result(nrBasisStates);
1342 for (
size_t i = 0; i < nrBasisStates; ++i)
1343 result[i] = ExtendedStabilizerBasisProbability(i);
1347 const Eigen::VectorXcd probs =
1348 simulationType == SimulationType::kMatrixProductState
1349 ? mpsSimulator->getRegisterStorage().cwiseAbs2()
1350 : state->getRegisterStorage().cwiseAbs2();
1352 std::vector<double> result(probs.size());
1354 for (
int i = 0; i < probs.size(); ++i) result[i] = probs[i].real();
1372 if (simulationType == SimulationType::kStabilizer)
1373 throw std::runtime_error(
1374 "QCSimState::Probabilities: Invalid simulation "
1375 "type for obtaining probabilities.");
1376 else if (simulationType == SimulationType::kTensorNetwork) {
1378 throw std::runtime_error(
1379 "QCSimState::Probabilities: Not implemented yet "
1380 "for the tensor network simulator.");
1383 std::vector<double> result(qubits.size());
1385 if (simulationType == SimulationType::kMatrixProductState) {
1386 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1387 result[i] = mpsSimulator->getBasisStateProbability(qubits[i]);
1388 }
else if (simulationType == SimulationType::kPauliPropagator) {
1389 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1390 result[i] = pp->Probability(qubits[i]);
1391 }
else if (simulationType == SimulationType::kPathIntegral) {
1392 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1393 result[i] = pathIntegralSimulator->Probability(qubits[i]);
1394 }
else if (simulationType == SimulationType::kDensityMatrix) {
1395 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1396 result[i] = densityMatrix->getBasisStateProbability(qubits[i]);
1397 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
1398 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1399 result[i] = mpoSimulator->getBasisStateProbability(qubits[i]);
1400 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
1401 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1402 result[i] = ExtendedStabilizerBasisProbability(qubits[i]);
1404 const Eigen::VectorXcd ® = state->getRegisterStorage();
1406 for (
int i = 0; i < static_cast<int>(qubits.size()); ++i)
1407 result[i] = std::norm(reg[qubits[i]]);
1429 std::unordered_map<Types::qubit_t, Types::qubit_t>
SampleCounts(
1431 if (qubits.empty() || shots == 0)
return {};
1433 if (qubits.size() >
sizeof(
size_t) * 8)
1435 <<
"Warning: The number of qubits to measure is larger than the "
1436 "number of bits in the size_t type, the outcome will be undefined"
1442 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
1446 if (simulationType == SimulationType::kMatrixProductState) {
1448 if (!configuration.IsSet(
"mps_sample_measure_algorithm") || configuration.GetConfiguration(
"mps_sample_measure_algorithm") ==
"mps_probabilities") {
1450 const std::set<Eigen::Index> qset(qubits.begin(), qubits.end());
1454 for (
size_t shot = 0; shot < shots; ++shot) {
1460 for (
auto q : qubits) {
1461 const size_t qubitMask = 1ULL << q;
1462 if (measRaw & qubitMask) meas |= mask;
1468 }
else if (qset.size() > 1) {
1469 mpsSimulator->MoveAtBeginningOfChain(qset);
1472 for (
size_t shot = 0; shot < shots; ++shot) {
1473 const auto measRaw = mpsSimulator->MeasureNoCollapse(qset);
1479 for (
auto q : qubits) {
1480 if (measRaw.at(q)) meas |= mask;
1487 }
else if (qset.size() == 1) {
1490 const auto prob0 = mpsSimulator->GetProbability(qubits[0]);
1491 for (
size_t shot = 0; shot < shots; ++shot) {
1492 const size_t meas = uniformZeroOne(rng) < prob0 ? 0ULL : 1ULL;
1495 for (
size_t i = 1; i < qubits.size(); ++i) {
1505 auto savedState = mpsSimulator->getState();
1506 for (
size_t shot = 0; shot < shots; ++shot) {
1507 const size_t meas =
Measure(qubits);
1509 mpsSimulator->setState(savedState);
1512 }
else if (simulationType == SimulationType::kStabilizer) {
1513 cliffordSimulator->SaveState();
1514 for (
size_t shot = 0; shot < shots; ++shot) {
1515 const size_t meas =
Measure(qubits);
1517 cliffordSimulator->RestoreState();
1519 cliffordSimulator->ClearSavedState();
1520 }
else if (simulationType == SimulationType::kTensorNetwork) {
1521 tensorNetwork->SaveState();
1522 for (
size_t shot = 0; shot < shots; ++shot) {
1523 const size_t meas =
Measure(qubits);
1525 tensorNetwork->RestoreState();
1527 tensorNetwork->ClearSavedState();
1528 }
else if (simulationType == SimulationType::kPauliPropagator) {
1529 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1530 for (
size_t shot = 0; shot < shots; ++shot) {
1531 const auto res = pp->Sample(qubitsInt);
1534 for (
size_t i = 0; i < qubits.size(); ++i) {
1535 if (res[i]) meas |= (1ULL << i);
1540 }
else if (simulationType == SimulationType::kPathIntegral) {
1541 if (nrQubits < 64) {
1543 const auto &litudes = pathIntegralSimulator->Amplitudes();
1546 for (
size_t shot = 0; shot < shots; ++shot) {
1547 const double prob = 1. - uniformZeroOne(rng);
1548 const size_t measRaw = alias.
Sample(prob);
1552 for (
auto q : qubits) {
1553 const size_t qubitMask = 1ULL << q;
1554 if ((measRaw & qubitMask) != 0) meas |= mask;
1564 for (
auto q : qubits) {
1565 const size_t qubitMask = 1ULL << q;
1566 if ((measRaw & qubitMask) != 0) meas |= mask;
1572 throw std::runtime_error(
1573 "QCSimState::SampleCounts: The path integral simulator does not "
1574 "support sampling for more than 63 qubits into 64 bits integers.");
1576 }
else if (simulationType == SimulationType::kDensityMatrix) {
1577 for (
size_t shot = 0; shot < shots; ++shot) {
1578 const size_t measured = densityMatrix->MeasureNoCollapse();
1580 for (
size_t i = 0; i < qubits.size(); ++i)
1581 if ((measured & (1ULL << qubits[i])) != 0) packed |= 1ULL << i;
1584 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
1585 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
1586 for (
size_t shot = 0; shot < shots; ++shot) {
1587 const auto measured = mpoSimulator->MeasureNoCollapse(qubitsSet);
1589 for (
size_t i = 0; i < qubits.size(); ++i)
1590 if (measured.at(
static_cast<Eigen::Index
>(qubits[i])))
1591 packed |= 1ULL << i;
1594 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
1595 auto sampler = extendedStabilizer->Clone();
1596 sampler->SaveState();
1597 for (
size_t shot = 0; shot < shots; ++shot) {
1598 sampler->RestoreState();
1600 for (
size_t i = 0; i < qubits.size(); ++i)
1601 if (sampler->Measure(qubits[i])) packed |= 1ULL << i;
1606 const auto &statev = state->getRegisterStorage();
1610 for (
size_t shot = 0; shot < shots; ++shot) {
1611 const double prob = 1. - uniformZeroOne(rng);
1612 const size_t measRaw = alias.
Sample(prob);
1616 for (
auto q : qubits) {
1617 const size_t qubitMask = 1ULL << q;
1618 if ((measRaw & qubitMask) != 0) meas |= mask;
1625 for (
size_t shot = 0; shot < shots; ++shot) {
1630 for (
auto q : qubits) {
1631 const size_t qubitMask = 1ULL << q;
1632 if ((measRaw & qubitMask) != 0) meas |= mask;
1642 NotifyObservers(qubits);
1660 std::unordered_map<std::vector<bool>,
Types::qubit_t> SampleCountsMany(
1662 if (qubits.empty() || shots == 0)
return {};
1668 if (simulationType == SimulationType::kMatrixProductState) {
1670 if (!configuration.IsSet(
"mps_sample_measure_algorithm") || configuration.GetConfiguration(
"mps_sample_measure_algorithm") ==
"mps_probabilities") {
1672 const std::set<Eigen::Index> qset(qubits.begin(), qubits.end());
1676 for (
size_t shot = 0; shot < shots; ++shot) {
1677 const auto meas = MeasureNoCollapseMany();
1681 std::vector<bool> measVec(qubits.size());
1682 for (
size_t i = 0; i < qubits.size(); ++i)
1683 measVec[i] = meas[qubits[i]];
1687 }
else if (qset.size() > 1) {
1688 mpsSimulator->MoveAtBeginningOfChain(qset);
1691 for (
size_t shot = 0; shot < shots; ++shot) {
1692 const auto meas = mpsSimulator->MeasureNoCollapse(qset);
1696 std::vector<bool> measVec(qubits.size());
1697 for (
size_t i = 0; i < qubits.size(); ++i)
1698 measVec[i] = meas.at(qubits[i]);
1702 }
else if (qset.size() == 1) {
1705 const auto prob0 = mpsSimulator->GetProbability(qubits[0]);
1706 for (
size_t shot = 0; shot < shots; ++shot) {
1707 const size_t meas = uniformZeroOne(rng) < prob0 ? 0ULL : 1ULL;
1708 const std::vector<bool> m(qubits.size(), meas);
1715 auto savedState = mpsSimulator->getState();
1716 for (
size_t shot = 0; shot < shots; ++shot) {
1717 const auto meas = MeasureMany(qubits);
1720 mpsSimulator->setState(savedState);
1723 }
else if (simulationType == SimulationType::kStabilizer) {
1724 cliffordSimulator->SaveState();
1725 for (
size_t shot = 0; shot < shots; ++shot) {
1726 const auto meas = MeasureMany(qubits);
1728 cliffordSimulator->RestoreState();
1730 cliffordSimulator->ClearSavedState();
1731 }
else if (simulationType == SimulationType::kTensorNetwork) {
1732 tensorNetwork->SaveState();
1733 for (
size_t shot = 0; shot < shots; ++shot) {
1734 const auto meas = MeasureMany(qubits);
1736 tensorNetwork->RestoreState();
1738 tensorNetwork->ClearSavedState();
1739 }
else if (simulationType == SimulationType::kPauliPropagator) {
1740 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1741 for (
size_t shot = 0; shot < shots; ++shot) {
1742 const auto meas = pp->Sample(qubitsInt);
1745 }
else if (simulationType == SimulationType::kPathIntegral) {
1746 if (nrQubits < 64) {
1748 const auto &litudes = pathIntegralSimulator->Amplitudes();
1750 for (
size_t shot = 0; shot < shots; ++shot) {
1751 const double prob = 1. - uniformZeroOne(rng);
1752 const size_t measRaw = alias.
Sample(prob);
1753 std::vector<bool> meas(qubits.size(),
false);
1754 for (
size_t i = 0; i < qubits.size(); ++i)
1755 if (((measRaw >> qubits[i]) & 1) == 1) meas[i] =
true;
1759 for (
size_t shot = 0; shot < shots; ++shot) {
1760 const auto measRaw = MeasureNoCollapseMany();
1761 std::vector<bool> meas(qubits.size(),
false);
1763 for (
size_t i = 0; i < qubits.size(); ++i)
1764 if (measRaw[qubits[i]]) meas[i] =
true;
1771 const auto &litudes = pathIntegralSimulator->Amplitudes();
1774 for (
size_t shot = 0; shot < shots; ++shot) {
1775 const double prob = 1. - uniformZeroOne(rng);
1776 const auto measRaw = alias.
Sample(prob);
1777 std::vector<bool> meas(qubits.size(),
false);
1778 for (
size_t i = 0; i < qubits.size(); ++i)
1779 if (measRaw.get(qubits[i])) meas[i] =
true;
1783 for (
size_t shot = 0; shot < shots; ++shot) {
1784 const auto measRaw = MeasureNoCollapseMany();
1785 std::vector<bool> meas(qubits.size(),
false);
1787 for (
size_t i = 0; i < qubits.size(); ++i)
1788 if (measRaw[qubits[i]]) meas[i] =
true;
1794 }
else if (simulationType == SimulationType::kDensityMatrix) {
1795 for (
size_t shot = 0; shot < shots; ++shot) {
1796 const size_t measured = densityMatrix->MeasureNoCollapse();
1797 std::vector<bool> packed(qubits.size(),
false);
1798 for (
size_t i = 0; i < qubits.size(); ++i)
1799 packed[i] = (measured & (1ULL << qubits[i])) != 0;
1802 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
1803 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
1804 for (
size_t shot = 0; shot < shots; ++shot) {
1805 const auto measured = mpoSimulator->MeasureNoCollapse(qubitsSet);
1806 std::vector<bool> packed(qubits.size(),
false);
1807 for (
size_t i = 0; i < qubits.size(); ++i)
1808 packed[i] = measured.at(
static_cast<Eigen::Index
>(qubits[i]));
1811 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
1812 auto sampler = extendedStabilizer->Clone();
1813 sampler->SaveState();
1814 for (
size_t shot = 0; shot < shots; ++shot) {
1815 sampler->RestoreState();
1816 std::vector<bool> packed(qubits.size(),
false);
1817 for (
size_t i = 0; i < qubits.size(); ++i)
1818 packed[i] = sampler->Measure(qubits[i]);
1823 const auto &statev = state->getRegisterStorage();
1827 for (
size_t shot = 0; shot < shots; ++shot) {
1828 const double prob = 1. - uniformZeroOne(rng);
1829 const size_t measRaw = alias.
Sample(prob);
1831 std::vector<bool> meas(qubits.size(),
false);
1833 for (
size_t i = 0; i < qubits.size(); ++i)
1834 if (((measRaw >> qubits[i]) & 1) == 1) meas[i] =
true;
1839 for (
size_t shot = 0; shot < shots; ++shot) {
1840 const auto measRaw = MeasureNoCollapseMany();
1841 std::vector<bool> meas(qubits.size(),
false);
1843 for (
size_t i = 0; i < qubits.size(); ++i)
1844 if (measRaw[qubits[i]]) meas[i] =
true;
1852 NotifyObservers(qubits);
1868 double ExpectationValue(
const std::string &pauliStringOrig)
override {
1869 if (pauliStringOrig.empty())
return 1.0;
1871 std::string pauliString = pauliStringOrig;
1874 const auto pauliOp = toupper(pauliString[i]);
1875 if (pauliOp !=
'I' && pauliOp !=
'Z')
return 0.0;
1881 if (simulationType == SimulationType::kStabilizer)
1882 return cliffordSimulator->ExpectationValue(pauliString);
1883 else if (simulationType == SimulationType::kTensorNetwork)
1884 return tensorNetwork->ExpectationValue(pauliString);
1885 else if (simulationType == SimulationType::kPauliPropagator)
1886 return pp->ExpectationValue(pauliString);
1887 else if (simulationType == SimulationType::kPathIntegral)
1888 return pathIntegralSimulator->ExpectationValue(pauliString);
1889 else if (simulationType == SimulationType::kDensityMatrix) {
1891 return densityMatrix->ExpectationValue(pauliString).real();
1893 else if (simulationType == SimulationType::kMatrixProductOperator) {
1895 return mpoSimulator->ExpectationValue(pauliString).real();
1897 else if (simulationType == SimulationType::kExtendedStabilizer)
1898 return extendedStabilizer->ExpectationValue(pauliString);
1901 static const QC::Gates::PauliXGate<> xgate;
1902 static const QC::Gates::PauliYGate<> ygate;
1903 static const QC::Gates::PauliZGate<> zgate;
1905 std::vector<QC::Gates::AppliedGate<Eigen::MatrixXcd>> pauliStringVec;
1906 pauliStringVec.reserve(pauliString.size());
1908 for (
size_t q = 0; q < pauliString.size(); ++q) {
1909 switch (toupper(pauliString[q])) {
1911 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1913 pauliStringVec.emplace_back(std::move(ag));
1916 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1918 pauliStringVec.emplace_back(std::move(ag));
1921 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1923 pauliStringVec.emplace_back(std::move(ag));
1932 if (pauliStringVec.empty())
return 1.0;
1934 if (simulationType == SimulationType::kMatrixProductState)
1935 return mpsSimulator->ExpectationValue(pauliStringVec).real();
1937 return state->ExpectationValue(pauliStringVec).real();
1947 SimulatorType GetType()
const override {
return SimulatorType::kQCSim; }
1967 void Flush()
override {}
1998 if (simulationType == SimulationType::kMatrixProductState)
1999 mpsSimulator->SaveState();
2000 else if (simulationType == SimulationType::kStabilizer)
2001 cliffordSimulator->SaveState();
2002 else if (simulationType == SimulationType::kTensorNetwork)
2003 tensorNetwork->SaveState();
2004 else if (simulationType == SimulationType::kPauliPropagator)
2006 else if (simulationType == SimulationType::kPathIntegral)
2007 pathIntegralSimulator->SaveState();
2008 else if (simulationType == SimulationType::kDensityMatrix)
2009 densityMatrix->SaveState();
2010 else if (simulationType == SimulationType::kMatrixProductOperator)
2011 mpoSimulator->SaveState();
2012 else if (simulationType == SimulationType::kExtendedStabilizer)
2013 extendedStabilizer->SaveState();
2027 if (simulationType == SimulationType::kMatrixProductState)
2028 mpsSimulator->RestoreState();
2029 else if (simulationType == SimulationType::kStabilizer)
2030 cliffordSimulator->RestoreState();
2031 else if (simulationType == SimulationType::kTensorNetwork)
2032 tensorNetwork->RestoreState();
2033 else if (simulationType == SimulationType::kPauliPropagator)
2035 else if (simulationType == SimulationType::kPathIntegral)
2036 pathIntegralSimulator->RestoreState();
2037 else if (simulationType == SimulationType::kDensityMatrix)
2038 densityMatrix->RestoreState();
2039 else if (simulationType == SimulationType::kMatrixProductOperator)
2040 mpoSimulator->RestoreState();
2041 else if (simulationType == SimulationType::kExtendedStabilizer)
2042 extendedStabilizer->RestoreState();
2044 state->RestoreState();
2054 std::complex<double> AmplitudeRaw(
Types::qubit_t outcome)
override {
2067 enableMultithreading = multithreading;
2068 if (state) state->SetMultithreading(multithreading);
2069 if (cliffordSimulator) cliffordSimulator->SetMultithreading(multithreading);
2070 if (tensorNetwork) tensorNetwork->SetMultithreading(multithreading);
2071 if (densityMatrix) densityMatrix->SetMultithreading(multithreading);
2074 pp->EnableParallel();
2076 pp->DisableParallel();
2078 if (pathIntegralSimulator) {
2079 enableMultithreading =
false;
2102 bool IsQcsim()
const override {
return true; }
2124 <<
"Warning: The number of qubits to measure is larger than the "
2125 "number of bits in the Types::qubit_t type, the outcome will be "
2129 if (simulationType == SimulationType::kStatevector)
2130 return state->MeasureNoCollapse();
2131 else if (simulationType == SimulationType::kDensityMatrix)
2132 return densityMatrix->MeasureNoCollapse();
2133 else if (simulationType == SimulationType::kMatrixProductOperator) {
2134 const auto measured = mpoSimulator->MeasureNoCollapse();
2136 for (
size_t qubit = 0; qubit < nrQubits; ++qubit)
2137 if (measured.at(
static_cast<Eigen::Index
>(qubit)))
2138 result |= 1ULL << qubit;
2141 else if (simulationType == SimulationType::kExtendedStabilizer) {
2142 auto sampler = extendedStabilizer->Clone();
2144 for (
size_t qubit = 0; qubit < nrQubits; ++qubit)
2145 if (sampler->Measure(qubit)) result |= 1ULL << qubit;
2148 else if (simulationType == SimulationType::kMatrixProductState) {
2149 const auto measured = mpsSimulator->MeasureNoCollapse();
2153 if (measured.at(q)) result |= mask;
2157 }
else if (simulationType == SimulationType::kPauliPropagator) {
2159 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
2160 const auto res = pp->Sample(qubitsInt);
2162 for (
size_t i = 0; i < res.size(); ++i) {
2163 if (res[i]) result |= (1ULL << i);
2166 }
else if (simulationType == SimulationType::kPathIntegral) {
2167 if (nrQubits < 64) {
2168 const auto measured = pathIntegralSimulator->MeasureNoCollapse();
2172 if (measured.get(q)) result |= mask;
2177 throw std::runtime_error(
2178 "QCSimState::MeasureNoCollapse: The path integral simulator does not "
2179 "support measuring more than 63 qubits into 64 bits integers.");
2183 throw std::runtime_error(
2184 "QCSimState::MeasureNoCollapse: Invalid simulation type for "
2186 "all the qubits without collapsing the state.");
2206 std::vector<bool> MeasureNoCollapseMany()
override {
2207 if (simulationType == SimulationType::kStatevector) {
2209 std::vector<bool> res(nrQubits);
2210 for (
size_t i = 0; i < nrQubits; ++i) res[i] = ((state >> i) & 1) == 1;
2212 }
else if (simulationType == SimulationType::kDensityMatrix) {
2213 const auto measured = densityMatrix->MeasureNoCollapse();
2214 std::vector<bool> res(nrQubits);
2215 for (
size_t i = 0; i < nrQubits; ++i)
2216 res[i] = ((measured >> i) & 1) == 1;
2218 }
else if (simulationType == SimulationType::kMatrixProductOperator) {
2219 const auto measured = mpoSimulator->MeasureNoCollapse();
2220 std::vector<bool> res(nrQubits);
2221 for (
size_t i = 0; i < nrQubits; ++i)
2222 res[i] = measured.at(
static_cast<Eigen::Index
>(i));
2224 }
else if (simulationType == SimulationType::kExtendedStabilizer) {
2225 auto sampler = extendedStabilizer->Clone();
2226 std::vector<bool> res(nrQubits);
2227 for (
size_t i = 0; i < nrQubits; ++i) res[i] = sampler->Measure(i);
2229 }
else if (simulationType == SimulationType::kMatrixProductState) {
2230 const auto measured = mpsSimulator->MeasureNoCollapse();
2231 std::vector<bool> res(nrQubits);
2232 for (
size_t i = 0; i < nrQubits; ++i) res[i] = measured.at(i);
2234 }
else if (simulationType == SimulationType::kPauliPropagator) {
2236 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
2237 return pp->Sample(qubitsInt);
2238 }
else if (simulationType == SimulationType::kPathIntegral) {
2239 const auto measured = pathIntegralSimulator->MeasureNoCollapse();
2240 std::vector<bool> res(nrQubits);
2241 for (
size_t i = 0; i < nrQubits; ++i) res[i] = measured.get(i);
2245 throw std::runtime_error(
2246 "QCSimState::MeasureNoCollapseMany: Invalid simulation type for "
2247 "measuring all the qubits without collapsing the state.");
2258 size_t GetCurrentMaxBondDimension()
const override {
return curMaxBondDim; }
2262 const std::unordered_map<std::string, std::string>& GetConfigMap()
2264 return configuration.GetConfigMap();
2268 const char* MaxBondDimensionConfigKey()
const {
2269 return simulationType == SimulationType::kMatrixProductOperator &&
2270 configuration.IsSet(
2271 "matrix_product_operator_max_bond_dimension")
2272 ?
"matrix_product_operator_max_bond_dimension"
2273 :
"matrix_product_state_max_bond_dimension";
2276 void ResetDummySimulator() {
2277 if (!dummySim)
return;
2279 std::vector<long long int> identityMap(nrQubits);
2280 for (
size_t qubit = 0; qubit < nrQubits; ++qubit)
2281 identityMap[qubit] =
static_cast<long long int>(qubit);
2282 dummySim->SetInitialQubitsMap(identityMap);
2283 dummySim->setTotalSwappingCost(0.);
2285 dummySim->SetCurrentBondDimensions(
2286 std::vector<double>(nrQubits - 1, 1.));
2289 size_t CheckedBasisStateCountForQueries()
const {
2290 if (nrQubits >= std::numeric_limits<size_t>::digits)
2291 throw std::runtime_error(
2292 "QCSimState: Too many qubits for enumerating basis states.");
2293 return 1ULL << nrQubits;
2296 double ExtendedStabilizerBasisProbability(
Types::qubit_t outcome)
const {
2297 const size_t nrBasisStates = CheckedBasisStateCountForQueries();
2298 if (outcome >= nrBasisStates)
return 0.0;
2300 double probability = 0.0;
2301 std::string pauliString(nrQubits,
'I');
2302 for (
size_t mask = 0; mask < nrBasisStates; ++mask) {
2304 size_t parityBits = mask &
static_cast<size_t>(outcome);
2305 while (parityBits != 0) {
2307 parityBits &= parityBits - 1;
2310 for (
size_t qubit = 0; qubit < nrQubits; ++qubit)
2311 pauliString[qubit] = ((mask >> qubit) & 1ULL) == 0 ?
'I' :
'Z';
2312 probability += sign * extendedStabilizer->ExpectationValue(pauliString);
2315 probability /=
static_cast<double>(nrBasisStates);
2316 return std::max(0.0, std::min(1.0, probability));
2319 SimulationType simulationType =
2320 SimulationType::kStatevector;
2322 std::unique_ptr<QC::QubitRegister<>> state;
2323 std::unique_ptr<QC::TensorNetworks::MPSSimulator>
2325 std::unique_ptr<QC::TensorNetworks::MPOSimulator>
2327 std::unique_ptr<QC::Clifford::StabilizerSimulator>
2329 std::unique_ptr<TensorNetworks::TensorNetwork>
2331 std::unique_ptr<QcsimPauliPropagator> pp;
2332 std::unique_ptr<PathIntegralSimulator>
2333 pathIntegralSimulator;
2334 std::unique_ptr<QC::DensityMatrix<>>
2336 std::unique_ptr<Simulators::QCSimExtendedStabilizer>
2339 size_t nrQubits = 0;
2341 bool enableMultithreading =
true;
2343 int lookaheadDepth = 0;
2344 int lookaheadDepthWithHeuristic = 0;
2345 bool useOptimalMeetingPosition =
true;
2346 std::vector<std::shared_ptr<Circuits::IOperation<>>> upcomingGates;
2347 long long int upcomingGateIndex = 0;
2348 double growthFactorSwap = 1.;
2349 double growthFactorGate = 0.65;
2351 std::unique_ptr<Simulators::MPSDummySimulator> dummySim;
2353 size_t curMaxBondDim = 0;
2354 QC::TensorNetworks::MPSSimulator::MeetingPositionCallback meetingPositionCallback =
nullptr;
2355 QC::TensorNetworks::MPSSimulator::BondDimensionCallback bondDimensionCallback =
nullptr;
2359 class GateCounterObserver :
public ISimulatorObserver {
2361 GateCounterObserver(
long long int &indexRef) : index(indexRef) {}
2365 long long int &index;
2367 std::shared_ptr<GateCounterObserver> gateCounterObserver;
2369 std::mt19937_64 rng;
2370 uint64_t nextSeedStream = 0;
2371 std::uniform_real_distribution<double> uniformZeroOne;
2373 Configuration configuration;
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
int RestoreState(void *sim)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
unsigned long int GetNumberOfQubits(void *sim)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int GetMultithreading(void *sim)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
double * Amplitude(void *sim, unsigned long long int outcome)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int SetMultithreading(void *sim, int multithreading)
int SaveStateToInternalDestructive(void *sim)
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 RestoreInternalDestructiveSavedState(void *sim)
QC::PathIntegral::FastVectorBool Sample(double v) const
size_t Sample(double v) const
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
uint_fast64_t qubit_t
The type of a qubit.