Maestro 0.2.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
QCSimState.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _QCSIMSTATE_H_
16#define _QCSIMSTATE_H_
17
18#ifdef INCLUDED_BY_FACTORY
19
20#include <algorithm>
21#include <iomanip>
22#include <limits>
23#include <sstream>
24#include <random>
25
26#include "Simulator.h"
27
28#include "Clifford.h"
29#include "MPSSimulator.h"
30#include "QubitRegister.h"
33
36
37#include "../Utils/Alias.h"
38
39#include "MPSDummySimulator.h"
40
41namespace Simulators {
42// TODO: Maybe use the pimpl idiom
43// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
44// for good but during development this should be good enough
45namespace Private {
46
57class QCSimState : public ISimulator {
58 public:
59 QCSimState() : rng(std::random_device{}()), uniformZeroOne(0, 1) {}
60
68 void Initialize() override {
69 if (nrQubits != 0) {
70 if (simulationType == SimulationType::kMatrixProductState) {
71 mpsSimulator =
72 std::make_unique<QC::TensorNetworks::MPSSimulator>(nrQubits);
73 if (limitEntanglement && singularValueThreshold > 0.)
74 mpsSimulator->setLimitEntanglement(singularValueThreshold);
75 if (limitSize && chi > 0) mpsSimulator->setLimitBondDimension(chi);
76 // default is true
77 if (!useOptimalMeetingPosition)
78 mpsSimulator->SetUseOptimalMeetingPosition(false);
79 } else if (simulationType == SimulationType::kStabilizer)
80 cliffordSimulator =
81 std::make_unique<QC::Clifford::StabilizerSimulator>(nrQubits);
82 else if (simulationType == SimulationType::kTensorNetwork) {
83 tensorNetwork =
84 std::make_unique<TensorNetworks::TensorNetwork>(nrQubits);
85 // for now the only used contractor is the forest one, but we'll use
86 // more in the future
87 const auto tensorContractor =
88 std::make_shared<TensorNetworks::ForestContractor>();
89 tensorNetwork->SetContractor(tensorContractor);
90 } else if (simulationType == SimulationType::kPauliPropagator) {
91 pp = std::make_unique<Simulators::QcsimPauliPropagator>();
92 pp->SetNrQubits(static_cast<int>(nrQubits));
93 if (ppCoefficientThreshold > 0.)
94 pp->SetCoefficientThreshold(ppCoefficientThreshold);
95 if (ppPauliWeightThreshold < std::numeric_limits<size_t>::max())
96 pp->SetPauliWeightThreshold(ppPauliWeightThreshold);
97 if (ppStepsBetweenTrims < std::numeric_limits<int>::max())
98 pp->SetStepsBetweenTrims(ppStepsBetweenTrims);
99 } else if (simulationType == SimulationType::kPathIntegral) {
100 pathIntegralSimulator = std::make_unique<PathIntegralSimulator>();
101 pathIntegralSimulator->SetStartZeroState(nrQubits);
102 } else
103 state = std::make_unique<QC::QubitRegister<>>(nrQubits);
104
105 SetMultithreading(enableMultithreading);
106 }
107 }
108
120 void InitializeState(size_t num_qubits,
121 std::vector<std::complex<double>> &amplitudes) override {
122 if (num_qubits == 0) return;
123 Clear();
124 nrQubits = num_qubits;
125 Initialize();
126 if (simulationType != SimulationType::kStatevector)
127 throw std::runtime_error(
128 "QCSimState::InitializeState: Invalid "
129 "simulation type for initializing the state.");
130
131 Eigen::VectorXcd amplitudesEigen(
132 Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(),
133 amplitudes.size()));
134 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
135 }
136
148 /*
149 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
150 avoid_init_allocator<std::complex<double>>>& amplitudes) override
151 {
152 Clear();
153 nrQubits = num_qubits;
154 Initialize();
155 Eigen::VectorXcd amplitudesEigen(Eigen::Map<Eigen::VectorXcd,
156 Eigen::Unaligned>(amplitudes.data(), amplitudes.size()));
157 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
158 }
159 */
160
172#ifndef NO_QISKIT_AER
173 void InitializeState(size_t num_qubits,
174 AER::Vector<std::complex<double>> &amplitudes) override {
175 if (num_qubits == 0) return;
176 Clear();
177 nrQubits = num_qubits;
178 Initialize();
179 if (simulationType != SimulationType::kStatevector)
180 throw std::runtime_error(
181 "QCSimState::InitializeState: Invalid "
182 "simulation type for initializing the state.");
183
184 Eigen::VectorXcd amplitudesEigen(
185 Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(),
186 amplitudes.size()));
187 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
188 }
189#endif
190
202 void InitializeState(size_t num_qubits,
203 Eigen::VectorXcd &amplitudes) override {
204 if (num_qubits == 0) return;
205 Clear();
206 nrQubits = num_qubits;
207 Initialize();
208
209 if (simulationType != SimulationType::kStatevector)
210 throw std::runtime_error(
211 "QCSimState::InitializeState: Invalid "
212 "simulation type for initializing the state.");
213
214 state = std::make_unique<QC::QubitRegister<>>(nrQubits, amplitudes);
215 state->SetMultithreading(enableMultithreading);
216 }
217
224 void Reset() override {
225 if (mpsSimulator)
226 mpsSimulator->Clear();
227 else if (cliffordSimulator)
228 cliffordSimulator->Reset();
229 else if (tensorNetwork)
230 tensorNetwork->Clear();
231 else if (state)
232 state->Reset();
233 else if (pp)
234 pp->ClearOperations();
235 else if (pathIntegralSimulator) {
236 pathIntegralSimulator->Reset();
237 pathIntegralSimulator->SetStartZeroState(nrQubits);
238 }
239
240 upcomingGateIndex = 0;
241 }
242
250 bool SupportsMPSSwapOptimization() const override { return true; }
251
260 void SetInitialQubitsMap(
261 const std::vector<long long int> &initialMap) override {
262 if (mpsSimulator) {
263 mpsSimulator->SetInitialQubitsMap(initialMap);
264 if (!dummySim || dummySim->getNrQubits() != initialMap.size()) {
265 dummySim =
266 std::make_unique<Simulators::MPSDummySimulator>(initialMap.size());
267 dummySim->SetMaxBondDimension(
268 limitSize ? static_cast<long long int>(chi) : 0);
269 }
270 dummySim->setGrowthFactorGate(growthFactorGate);
271 dummySim->setGrowthFactorSwap(growthFactorSwap);
272 dummySim->SetInitialQubitsMap(initialMap);
273 }
274 }
275
276 void SetUseOptimalMeetingPosition(bool enable) override {
277 useOptimalMeetingPosition = enable;
278 if (mpsSimulator) mpsSimulator->SetUseOptimalMeetingPosition(enable);
279 }
280
281 void SetLookaheadDepth(int depth) override {
282 lookaheadDepth = depth;
283 if (mpsSimulator && depth > 0 && !useOptimalMeetingPosition)
284 mpsSimulator->SetUseOptimalMeetingPosition(true);
285 }
286
287 void SetLookaheadDepthWithHeuristic(int depth) override {
288 lookaheadDepthWithHeuristic = depth;
289 if (lookaheadDepth < depth) SetLookaheadDepth(depth);
290 }
291
292 void SetUpcomingGates(
293 const std::vector<std::shared_ptr<Circuits::IOperation<double>>> &gates)
294 override {
295 upcomingGates = gates;
296 upcomingGateIndex = 0;
297
298 if (!mpsSimulator || lookaheadDepth <= 0 || lookaheadDepth == std::numeric_limits<int>::max()) return;
299
300 // Register an observer that advances the gate index
301 ClearObservers(); // for now we only have this observer, so this should be
302 // fine
303 gateCounterObserver =
304 std::make_shared<GateCounterObserver>(upcomingGateIndex);
305 RegisterObserver(gateCounterObserver);
306
307 // Set up a meeting position callback that uses MPSDummySimulator
308 // for lookahead evaluation with actual bond dimensions
309 // the callback is called only for two qubits gates and only if executing
310 // them would require a swap
311 mpsSimulator->SetMeetingPositionCallback(
312 [this](/*const auto &qMap,*/ const auto &bondDims)
313 -> QC::TensorNetworks::MPSSimulatorInterface::IndexType {
314 if (upcomingGates.empty() ||
315 upcomingGateIndex >= upcomingGates.size()) {
316 return -1; // will fallback to default behavior
317 }
318
319 const size_t nQ = bondDims.size() + 1;
320
321 if (!dummySim || dummySim->getNrQubits() != nQ) {
322 dummySim = std::make_unique<Simulators::MPSDummySimulator>(nQ);
323 dummySim->SetMaxBondDimension(
324 limitSize ? static_cast<long long int>(chi) : 0);
325 dummySim->setGrowthFactorGate(growthFactorGate);
326 dummySim->setGrowthFactorSwap(growthFactorSwap);
327 }
328
329 // Seed dummy with current real simulator state
330 // std::vector<long long int> map64(qMap.begin(), qMap.end());
331 // dummySim->SetInitialQubitsMap(map64);
332 dummySim->setTotalSwappingCost(0);
333
334 // check qubits map:
335 /*
336 auto qbitmMap = dummySim->getQubitsMap();
337 for (size_t i = 0; i < nQ; ++i) {
338 if (qbitmMap[i] != qMap[i]) {
339 std::cerr << "Error: qubits map mismatch at index " << i
340 << ": dummySim has " << qbitmMap[i]
341 << " but real sim has " << qMap[i] << std::endl;
342 exit(0);
343 }
344 }
345 */
346
347 // check them, they should be the same, otherwise something is wrong
348
349 // Convert actual bond dims to doubles
350 std::vector<double> bondDimsD(bondDims.begin(), bondDims.end());
351 dummySim->SetCurrentBondDimensions(bondDimsD);
352
353 // display bond dimensions for debugging
354 /*
355 std::cout << "Bond dimensions before swapping and applying the gate:
356 "; for (size_t i = 0; i < bondDims.size(); ++i) { std::cout <<
357 bondDims[i] << " ";
358 }
359 std::cout << std::endl;
360 */
361
362 const auto &op = upcomingGates[upcomingGateIndex];
363 const auto qbits = op->AffectedQubits();
364
365 if (qbits.size() != 2) {
366 std::cerr << "Error: Meeting position callback called for a gate "
367 "that does not have exactly 2 qubits."
368 << std::endl;
369
370 return -1; // will fallback
371 }
372
373 /*
374 const auto &qmap = dummySim->getQubitsMap();
375
376 std::cout << "Applying 2-qubit gate on physical qubits " <<
377 qmap[qbits[0]] << " and "
378 << qmap[qbits[1]]
379 << std::endl;
380 */
381 /*
382 std::cout << "Finding best meeting position for upcoming gates
383 starting at index "
384 << upcomingGateIndex << " with lookahead depth " <<
385 lookaheadDepth << " and heuristic depth "
386 << lookaheadDepthWithHeuristic << std::endl;
387
388
389
390 std::cout << "Affected qubits: ";
391 for (const auto &q : qbits) std::cout << q << " ";
392 std::cout << std::endl;
393
394 std::cout << "Current qubits map: ";
395 for (size_t i = 0; i < qMap.size(); ++i) std::cout << qMap[i] << " ";
396 std::cout << std::endl;
397
398 std::cout << "Current inverse qubits map: ";
399 for (size_t i = 0; i < qMapInv.size(); ++i) std::cout << qMapInv[i] <<
400 " "; std::cout << std::endl;
401 */
402
403 double bestCost = std::numeric_limits<double>::infinity();
404 auto res = dummySim->FindBestMeetingPosition(
405 upcomingGates, upcomingGateIndex, lookaheadDepth,
406 lookaheadDepthWithHeuristic, 0, bestCost);
407
408 // std::cout << "Swapping the two qubits on position: " << res << "
409 // and " << (res + 1) << std::endl;
410
411 dummySim->SwapQubitsToPosition(qbits[0], qbits[1], res);
412 dummySim->ApplyGate(op);
413
414 // display the expected bond dimensions after applying the gate for
415 // debugging
416
417 /*
418 const auto &expectedBondDims = dummySim->getCurrentBondDimensions();
419 std::cout << "Expected bond dimensions after swapping and applying "
420 "the gate: ";
421 for (size_t i = 0; i < expectedBondDims.size(); ++i) {
422 std::cout << expectedBondDims[i] << " ";
423 }
424 std::cout << std::endl;
425 */
426
427 // std::cout << "Best meeting position: " << res
428 // << " with estimated cost: " << bestCost << std::endl;
429
430 return res;
431 });
432 }
433
442 long long int GetGatesCounter() const override { return upcomingGateIndex; }
443
453 void SetGatesCounter(long long int counter) override {
454 upcomingGateIndex = counter;
455 }
456
465 void IncrementGatesCounter() override { ++upcomingGateIndex; }
466
467 double getGrowthFactorSwap() const override { return growthFactorSwap; }
468 double getGrowthFactorGate() const override { return growthFactorGate; }
469
470 void setGrowthFactorSwap(double factor) override {
471 growthFactorSwap = factor;
472 if (dummySim) dummySim->setGrowthFactorSwap(factor);
473 }
474
475 void setGrowthFactorGate(double factor) override {
476 growthFactorGate = factor;
477 if (dummySim) dummySim->setGrowthFactorGate(factor);
478 }
479
488 void Configure(const char *key, const char *value) override {
489 if (std::string("method") == key) {
490 if (std::string("statevector") == value)
491 simulationType = SimulationType::kStatevector;
492 else if (std::string("matrix_product_state") == value)
493 simulationType = SimulationType::kMatrixProductState;
494 else if (std::string("stabilizer") == value)
495 simulationType = SimulationType::kStabilizer;
496 else if (std::string("tensor_network") == value)
497 simulationType = SimulationType::kTensorNetwork;
498 else if (std::string("pauli_propagator") == value)
499 simulationType = SimulationType::kPauliPropagator;
500 else if (std::string("path_integral") == value)
501 simulationType = SimulationType::kPathIntegral;
502 } else if (std::string("matrix_product_state_truncation_threshold") ==
503 key) {
504 singularValueThreshold = std::stod(value);
505 if (singularValueThreshold > 0.) {
506 limitEntanglement = true;
507 if (mpsSimulator)
508 mpsSimulator->setLimitEntanglement(singularValueThreshold);
509 } else
510 limitEntanglement = false;
511 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
512 chi = std::stoi(value);
513 if (chi > 0) {
514 limitSize = true;
515 if (mpsSimulator) mpsSimulator->setLimitBondDimension(chi);
516 if (dummySim)
517 dummySim->SetMaxBondDimension(static_cast<long long int>(chi));
518 } else {
519 limitSize = false;
520 if (mpsSimulator) mpsSimulator->setLimitBondDimension(0);
521 if (dummySim) dummySim->SetMaxBondDimension(0);
522 }
523 } else if (std::string("mps_sample_measure_algorithm") == key)
524 useMPSMeasureNoCollapse = std::string("mps_probabilities") == value;
525 else if (std::string("pauli_propagator_coefficient_threshold") == key) {
526 ppCoefficientThreshold = std::stod(value);
527 if (pp && ppCoefficientThreshold > 0.)
528 pp->SetCoefficientThreshold(ppCoefficientThreshold);
529 } else if (std::string("pauli_propagator_pauli_weight_threshold") == key) {
530 ppPauliWeightThreshold = std::stoull(value);
531 if (pp && ppPauliWeightThreshold < std::numeric_limits<size_t>::max())
532 pp->SetPauliWeightThreshold(ppPauliWeightThreshold);
533 } else if (std::string("pauli_propagator_steps_between_trims") == key) {
534 ppStepsBetweenTrims = std::stoi(value);
535 if (pp && ppStepsBetweenTrims < std::numeric_limits<int>::max())
536 pp->SetStepsBetweenTrims(ppStepsBetweenTrims);
537 }
538 }
539
547 std::string GetConfiguration(const char *key) const override {
548 if (std::string("method") == key) {
549 switch (simulationType) {
550 case SimulationType::kStatevector:
551 return "statevector";
552 case SimulationType::kMatrixProductState:
553 return "matrix_product_state";
554 case SimulationType::kStabilizer:
555 return "stabilizer";
556 case SimulationType::kTensorNetwork:
557 return "tensor_network";
558 case SimulationType::kPauliPropagator:
559 return "pauli_propagator";
560 case SimulationType::kPathIntegral:
561 return "path_integral";
562 default:
563 return "other";
564 }
565 } else if (std::string("matrix_product_state_truncation_threshold") ==
566 key) {
567 if (limitEntanglement && singularValueThreshold > 0.) {
568 std::ostringstream oss;
569 oss << std::setprecision(std::numeric_limits<double>::max_digits10)
570 << singularValueThreshold;
571 return oss.str();
572 }
573 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
574 if (limitSize && chi > 0) return std::to_string(chi);
575 } else if (std::string("mps_sample_measure_algorithm") == key) {
576 return useMPSMeasureNoCollapse ? "mps_probabilities"
577 : "mps_apply_measure";
578 }
579
580 return "";
581 }
582
590 size_t AllocateQubits(size_t num_qubits) override {
591 if ((simulationType == SimulationType::kStatevector && state) ||
592 (simulationType == SimulationType::kMatrixProductState &&
593 mpsSimulator) ||
594 (simulationType == SimulationType::kStabilizer && cliffordSimulator) ||
595 (simulationType == SimulationType::kTensorNetwork && tensorNetwork))
596 return 0;
597
598 const size_t oldNrQubits = nrQubits;
599 nrQubits += num_qubits;
600 if (simulationType == SimulationType::kPauliPropagator)
601 if (pp) pp->SetNrQubits(static_cast<int>(nrQubits));
602
603 return oldNrQubits;
604 }
605
612 size_t GetNumberOfQubits() const override { return nrQubits; }
613
621 void Clear() override {
622 state = nullptr;
623 mpsSimulator = nullptr;
624 cliffordSimulator = nullptr;
625 tensorNetwork = nullptr;
626 pp = nullptr;
627 pathIntegralSimulator = nullptr;
628 dummySim = nullptr;
629 nrQubits = 0;
630 upcomingGateIndex = 0;
631 upcomingGates.clear();
632 }
633
644 size_t Measure(const Types::qubits_vector &qubits) override {
645 // TODO: this is inefficient, maybe implement it better in qcsim
646 // for now it has the possibility of measuring a qubits interval, but not a
647 // list of qubits
648 if (qubits.size() > sizeof(size_t) * 8)
649 std::cerr
650 << "Warning: The number of qubits to measure is larger than the "
651 "number of bits in the size_t type, the outcome will be undefined"
652 << std::endl;
653
654 size_t res = 0;
655 size_t mask = 1ULL;
656
657 DontNotify();
658 if (simulationType == SimulationType::kStatevector) {
659 for (size_t qubit : qubits) {
660 if (state->MeasureQubit(static_cast<unsigned int>(qubit))) res |= mask;
661 mask <<= 1;
662 }
663 } else if (simulationType == SimulationType::kStabilizer) {
664 for (size_t qubit : qubits) {
665 if (cliffordSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
666 res |= mask;
667 mask <<= 1;
668 }
669 } else if (simulationType == SimulationType::kTensorNetwork) {
670 for (size_t qubit : qubits) {
671 if (tensorNetwork->Measure(static_cast<unsigned int>(qubit)))
672 res |= mask;
673 mask <<= 1;
674 }
675 } else if (simulationType == SimulationType::kPauliPropagator) {
676 std::vector<int> qubitsInt;
677 qubitsInt.reserve(qubits.size());
678 for (const auto q : qubits)
679 qubitsInt.push_back(static_cast<int>(q));
680 const auto res = pp->Measure(qubitsInt);
681 Types::qubit_t result = 0;
682 for (size_t i = 0; i < res.size(); ++i) {
683 if (res[i]) result |= mask;
684 mask <<= 1;
685 }
686 return result;
687 } else if (simulationType == SimulationType::kPathIntegral) {
688 for (size_t qubit : qubits) {
689 if (pathIntegralSimulator->MeasureQubit(qubit)) res |= mask;
690 mask <<= 1;
691 }
692 } else {
693 /*
694 for (size_t qubit : qubits)
695 {
696 if (mpsSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
697 res |= mask;
698 mask <<= 1;
699 }
700 */
701 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
702 auto measured = mpsSimulator->MeasureQubits(qubitsSet);
703 for (Types::qubit_t qubit : qubits) {
704 if (measured[qubit]) res |= mask;
705 mask <<= 1;
706 }
707 }
708 Notify();
709
710 NotifyObservers(qubits);
711
712 return res;
713 }
714
721 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
722 std::vector<bool> res(qubits.size(), false);
723 DontNotify();
724
725 if (simulationType == SimulationType::kStatevector) {
726 for (size_t q = 0; q < qubits.size(); ++q)
727 if (state->MeasureQubit(static_cast<unsigned int>(qubits[q])))
728 res[q] = true;
729 } else if (simulationType == SimulationType::kStabilizer) {
730 for (size_t q = 0; q < qubits.size(); ++q)
731 if (cliffordSimulator->MeasureQubit(
732 static_cast<unsigned int>(qubits[q])))
733 res[q] = true;
734 } else if (simulationType == SimulationType::kTensorNetwork) {
735 for (size_t q = 0; q < qubits.size(); ++q)
736 if (tensorNetwork->Measure(static_cast<unsigned int>(qubits[q])))
737 res[q] = true;
738 } else if (simulationType == SimulationType::kPauliPropagator) {
739 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
740 res = pp->Measure(qubitsInt);
741 } else if (simulationType == SimulationType::kPathIntegral) {
742 for (size_t q = 0; q < qubits.size(); ++q)
743 if (pathIntegralSimulator->MeasureQubit(qubits[q])) res[q] = true;
744 } else {
745 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
746 auto measured = mpsSimulator->MeasureQubits(qubitsSet);
747 for (size_t q = 0; q < qubits.size(); ++q)
748 if (measured[qubits[q]]) res[q] = true;
749 }
750 Notify();
751 NotifyObservers(qubits);
752
753 return res;
754 }
755
762 void ApplyReset(const Types::qubits_vector &qubits) override {
763 QC::Gates::PauliXGate xGate;
764
765 DontNotify();
766 if (simulationType == SimulationType::kStatevector) {
767 for (size_t qubit : qubits)
768 if (state->MeasureQubit(static_cast<unsigned int>(qubit)))
769 state->ApplyGate(xGate, static_cast<unsigned int>(qubit));
770 } else if (simulationType == SimulationType::kStabilizer) {
771 for (size_t qubit : qubits)
772 if (cliffordSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
773 cliffordSimulator->ApplyX(static_cast<unsigned int>(qubit));
774 } else if (simulationType == SimulationType::kTensorNetwork) {
775 for (size_t qubit : qubits)
776 if (tensorNetwork->Measure(static_cast<unsigned int>(qubit)))
777 tensorNetwork->AddGate(xGate, static_cast<unsigned int>(qubit));
778 } else if (simulationType == SimulationType::kPauliPropagator) {
779 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
780 const auto res = pp->Measure(qubitsInt);
781 for (size_t i = 0; i < res.size(); ++i) {
782 if (res[i]) pp->ApplyX(qubitsInt[i]);
783 }
784 } else if (simulationType == SimulationType::kPathIntegral) {
785 for (size_t qubit : qubits)
786 if (pathIntegralSimulator->MeasureQubit(qubit)) {
787 QC::Gates::AppliedGate<> gate(xGate.getRawOperatorMatrix(), qubit);
788 pathIntegralSimulator->PropagateStep(
789 gate, pathIntegralSimulator->Amplitudes());
790 }
791 } else {
792 for (size_t qubit : qubits)
793 if (mpsSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
794 mpsSimulator->ApplyGate(xGate, static_cast<unsigned int>(qubit));
795 }
796 Notify();
797
798 NotifyObservers(qubits);
799 }
800
812 double Probability(Types::qubit_t outcome) override {
813 if (simulationType == SimulationType::kMatrixProductState)
814 return mpsSimulator->getBasisStateProbability(
815 static_cast<unsigned int>(outcome));
816 else if (simulationType == SimulationType::kStabilizer)
817 return cliffordSimulator->getBasisStateProbability(
818 static_cast<unsigned int>(outcome));
819 else if (simulationType == SimulationType::kTensorNetwork)
820 return tensorNetwork->getBasisStateProbability(outcome);
821 else if (simulationType == SimulationType::kPauliPropagator)
822 return pp->Probability(outcome);
823 else if (simulationType == SimulationType::kPathIntegral)
824 return pathIntegralSimulator->Probability(outcome);
825
826 return state->getBasisStateProbability(static_cast<unsigned int>(outcome));
827 }
828
839 std::complex<double> Amplitude(Types::qubit_t outcome) override {
840 if (simulationType == SimulationType::kMatrixProductState)
841 return mpsSimulator->getBasisStateAmplitude(
842 static_cast<unsigned int>(outcome));
843 else if (simulationType == SimulationType::kPathIntegral)
844 return pathIntegralSimulator->AmplitudeForOutcome(outcome);
845 else if (simulationType == SimulationType::kStabilizer)
846 throw std::runtime_error(
847 "QCSimState::Amplitude: Invalid simulation type for obtaining the "
848 "amplitude of the specified outcome.");
849 else if (simulationType == SimulationType::kTensorNetwork)
850 throw std::runtime_error(
851 "QCSimState::Amplitude: Not supported for the "
852 "tensor network simulator.");
853 else if (simulationType == SimulationType::kPauliPropagator)
854 throw std::runtime_error(
855 "QCSimState::Amplitude: Invalid simulation type for obtaining the "
856 "amplitude of the specified outcome.");
857
858 return state->getBasisStateAmplitude(static_cast<unsigned int>(outcome));
859 }
860
874 std::complex<double> ProjectOnZero() override {
875 if (simulationType == SimulationType::kMatrixProductState)
876 return mpsSimulator->ProjectOnZero();
877
878 return Amplitude(0);
879 }
880
891 std::vector<double> AllProbabilities() override {
892 // TODO: In principle this could be done, but why? It should be costly.
893 if (simulationType == SimulationType::kTensorNetwork)
894 throw std::runtime_error(
895 "QCSimState::AllProbabilities: Invalid "
896 "simulation type for obtaining probabilities.");
897 else if (simulationType == SimulationType::kStabilizer)
898 return cliffordSimulator->AllProbabilities();
899 else if (simulationType == SimulationType::kPauliPropagator) {
900 const size_t nrBasisStates = 1ULL << GetNumberOfQubits();
901 std::vector<double> result(nrBasisStates);
902 for (size_t i = 0; i < nrBasisStates; ++i) result[i] = pp->Probability(i);
903 return result;
904 } else if (simulationType == SimulationType::kPathIntegral) {
905 const size_t nrBasisStates = 1ULL << GetNumberOfQubits();
906 std::vector<double> result(nrBasisStates);
907 for (size_t i = 0; i < nrBasisStates; ++i)
908 result[i] = pathIntegralSimulator->Probability(i);
909 return result;
910 }
911
912 const Eigen::VectorXcd probs =
913 simulationType == SimulationType::kMatrixProductState
914 ? mpsSimulator->getRegisterStorage().cwiseAbs2()
915 : state->getRegisterStorage().cwiseAbs2();
916
917 std::vector<double> result(probs.size());
918
919 for (int i = 0; i < probs.size(); ++i) result[i] = probs[i].real();
920
921 return result;
922 }
923
935 std::vector<double> Probabilities(
936 const Types::qubits_vector &qubits) override {
937 if (simulationType == SimulationType::kStabilizer)
938 throw std::runtime_error(
939 "QCSimState::Probabilities: Invalid simulation "
940 "type for obtaining probabilities.");
941 else if (simulationType == SimulationType::kTensorNetwork) {
942 // TODO: Implement this!!!
943 throw std::runtime_error(
944 "QCSimState::Probabilities: Not implemented yet "
945 "for the tensor network simulator.");
946 }
947
948 std::vector<double> result(qubits.size());
949
950 if (simulationType == SimulationType::kMatrixProductState) {
951 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
952 result[i] = mpsSimulator->getBasisStateProbability(qubits[i]);
953 } else if (simulationType == SimulationType::kPauliPropagator) {
954 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
955 result[i] = pp->Probability(qubits[i]);
956 } else if (simulationType == SimulationType::kPathIntegral) {
957 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
958 result[i] = pathIntegralSimulator->Probability(qubits[i]);
959 } else {
960 const Eigen::VectorXcd &reg = state->getRegisterStorage();
961
962 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
963 result[i] = std::norm(reg[qubits[i]]);
964 }
965
966 return result;
967 }
968
985 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
986 const Types::qubits_vector &qubits, size_t shots = 1000) override {
987 if (qubits.empty() || shots == 0) return {};
988
989 if (qubits.size() > sizeof(size_t) * 8)
990 std::cerr
991 << "Warning: The number of qubits to measure is larger than the "
992 "number of bits in the size_t type, the outcome will be undefined"
993 << std::endl;
994
995 // TODO: this is inefficient, maybe implement it better in qcsim
996 // for now it has the possibility of measuring a qubits interval, but not a
997 // list of qubits
998 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
999
1000 DontNotify();
1001
1002 if (simulationType == SimulationType::kMatrixProductState) {
1003 bool normal = true;
1004 if (useMPSMeasureNoCollapse) {
1005 // check to see if it can be used
1006 const std::set<Eigen::Index> qset(qubits.begin(), qubits.end());
1007 if (qset.size() == GetNumberOfQubits()) {
1008 // it can!
1009 normal = false;
1010 for (size_t shot = 0; shot < shots; ++shot) {
1011 const size_t measRaw = MeasureNoCollapse();
1012 size_t meas = 0;
1013 size_t mask = 1ULL;
1014
1015 // translate the measurement
1016 for (auto q : qubits) {
1017 const size_t qubitMask = 1ULL << q;
1018 if (measRaw & qubitMask) meas |= mask;
1019 mask <<= 1ULL;
1020 }
1021
1022 ++result[meas];
1023 }
1024 } else if (qset.size() > 1) {
1025 mpsSimulator->MoveAtBeginningOfChain(qset);
1026 // now sample
1027 normal = false;
1028 for (size_t shot = 0; shot < shots; ++shot) {
1029 const auto measRaw = mpsSimulator->MeasureNoCollapse(qset);
1030 size_t meas = 0;
1031 size_t mask = 1ULL;
1032
1033 // might not be in the requested order
1034 // translate the measurement
1035 for (auto q : qubits) {
1036 if (measRaw.at(q)) meas |= mask;
1037 mask <<= 1ULL;
1038 }
1039
1040 ++result[meas];
1041 }
1042
1043 } else if (qset.size() == 1) {
1044 // if only one qubit is measured, we can use the probability
1045 normal = false;
1046 const auto prob0 = mpsSimulator->GetProbability(qubits[0]);
1047 for (size_t shot = 0; shot < shots; ++shot) {
1048 const size_t meas = uniformZeroOne(rng) < prob0 ? 0ULL : 1ULL;
1049 size_t m = meas;
1050 // why would somebody set more than one time?
1051 for (size_t i = 1; i < qubits.size(); ++i) {
1052 m <<= 1ULL;
1053 m |= meas;
1054 }
1055 ++result[m];
1056 }
1057 }
1058 }
1059
1060 if (normal) {
1061 auto savedState = mpsSimulator->getState();
1062 for (size_t shot = 0; shot < shots; ++shot) {
1063 const size_t meas = Measure(qubits);
1064 ++result[meas];
1065 mpsSimulator->setState(savedState);
1066 }
1067 }
1068 } else if (simulationType == SimulationType::kStabilizer) {
1069 cliffordSimulator->SaveState();
1070 for (size_t shot = 0; shot < shots; ++shot) {
1071 const size_t meas = Measure(qubits);
1072 ++result[meas];
1073 cliffordSimulator->RestoreState();
1074 }
1075 cliffordSimulator->ClearSavedState();
1076 } else if (simulationType == SimulationType::kTensorNetwork) {
1077 tensorNetwork->SaveState();
1078 for (size_t shot = 0; shot < shots; ++shot) {
1079 const size_t meas = Measure(qubits);
1080 ++result[meas];
1081 tensorNetwork->RestoreState();
1082 }
1083 tensorNetwork->ClearSavedState();
1084 } else if (simulationType == SimulationType::kPauliPropagator) {
1085 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1086 for (size_t shot = 0; shot < shots; ++shot) {
1087 const auto res = pp->Sample(qubitsInt);
1088
1089 size_t meas = 0;
1090 for (size_t i = 0; i < qubits.size(); ++i) {
1091 if (res[i]) meas |= (1ULL << i);
1092 }
1093
1094 ++result[meas];
1095 }
1096 } else if (simulationType == SimulationType::kPathIntegral) {
1097 if (nrQubits < 64) {
1098 if (shots > 1) {
1099 const auto &amplitudes = pathIntegralSimulator->Amplitudes();
1100 const Utils::Alias alias(amplitudes);
1101
1102 for (size_t shot = 0; shot < shots; ++shot) {
1103 const double prob = 1. - uniformZeroOne(rng);
1104 const size_t measRaw = alias.Sample(prob);
1105
1106 size_t meas = 0;
1107 size_t mask = 1ULL;
1108 for (auto q : qubits) {
1109 const size_t qubitMask = 1ULL << q;
1110 if ((measRaw & qubitMask) != 0) meas |= mask;
1111 mask <<= 1ULL;
1112 }
1113
1114 ++result[meas];
1115 }
1116 } else {
1117 const size_t measRaw = MeasureNoCollapse();
1118 size_t meas = 0;
1119 size_t mask = 1ULL;
1120 for (auto q : qubits) {
1121 const size_t qubitMask = 1ULL << q;
1122 if ((measRaw & qubitMask) != 0) meas |= mask;
1123 mask <<= 1ULL;
1124 }
1125 ++result[meas];
1126 }
1127 } else {
1128 throw std::runtime_error(
1129 "QCSimState::SampleCounts: The path integral simulator does not "
1130 "support sampling for more than 63 qubits into 64 bits integers.");
1131 }
1132 } else {
1133 if (shots > 1) {
1134 const auto &statev = state->getRegisterStorage();
1135
1136 const Utils::Alias alias(statev);
1137
1138 for (size_t shot = 0; shot < shots; ++shot) {
1139 const double prob = 1. - uniformZeroOne(rng);
1140 const size_t measRaw = alias.Sample(prob);
1141
1142 size_t meas = 0;
1143 size_t mask = 1ULL;
1144 for (auto q : qubits) {
1145 const size_t qubitMask = 1ULL << q;
1146 if ((measRaw & qubitMask) != 0) meas |= mask;
1147 mask <<= 1ULL;
1148 }
1149
1150 ++result[meas];
1151 }
1152 } else {
1153 for (size_t shot = 0; shot < shots; ++shot) {
1154 const size_t measRaw = MeasureNoCollapse();
1155 size_t meas = 0;
1156 size_t mask = 1ULL;
1157
1158 for (auto q : qubits) {
1159 const size_t qubitMask = 1ULL << q;
1160 if ((measRaw & qubitMask) != 0) meas |= mask;
1161 mask <<= 1ULL;
1162 }
1163
1164 ++result[meas];
1165 }
1166 }
1167 }
1168
1169 Notify();
1170 NotifyObservers(qubits);
1171
1172 return result;
1173 }
1174
1188 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
1189 const Types::qubits_vector &qubits, size_t shots = 1000) override {
1190 if (qubits.empty() || shots == 0) return {};
1191
1192 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
1193
1194 DontNotify();
1195
1196 if (simulationType == SimulationType::kMatrixProductState) {
1197 bool normal = true;
1198 if (useMPSMeasureNoCollapse) {
1199 // check to see if it can be used
1200 const std::set<Eigen::Index> qset(qubits.begin(), qubits.end());
1201 if (qset.size() == GetNumberOfQubits()) {
1202 // it can!
1203 normal = false;
1204 for (size_t shot = 0; shot < shots; ++shot) {
1205 const auto meas = MeasureNoCollapseMany();
1206
1207 // might not be in the requested order
1208 // translate the measurement
1209 std::vector<bool> measVec(qubits.size());
1210 for (size_t i = 0; i < qubits.size(); ++i)
1211 measVec[i] = meas[qubits[i]];
1212
1213 ++result[measVec];
1214 }
1215 } else if (qset.size() > 1) {
1216 mpsSimulator->MoveAtBeginningOfChain(qset);
1217 // now sample
1218 normal = false;
1219 for (size_t shot = 0; shot < shots; ++shot) {
1220 const auto meas = mpsSimulator->MeasureNoCollapse(qset);
1221
1222 // might not be in the requested order
1223 // translate the measurement
1224 std::vector<bool> measVec(qubits.size());
1225 for (size_t i = 0; i < qubits.size(); ++i)
1226 measVec[i] = meas.at(qubits[i]);
1227
1228 ++result[measVec];
1229 }
1230 } else if (qset.size() == 1) {
1231 // if only one qubit is measured, we can use the probability
1232 normal = false;
1233 const auto prob0 = mpsSimulator->GetProbability(qubits[0]);
1234 for (size_t shot = 0; shot < shots; ++shot) {
1235 const size_t meas = uniformZeroOne(rng) < prob0 ? 0ULL : 1ULL;
1236 const std::vector<bool> m(qubits.size(), meas);
1237 ++result[m];
1238 }
1239 }
1240 }
1241
1242 if (normal) {
1243 auto savedState = mpsSimulator->getState();
1244 for (size_t shot = 0; shot < shots; ++shot) {
1245 const auto meas = MeasureMany(qubits);
1246
1247 ++result[meas];
1248 mpsSimulator->setState(savedState);
1249 }
1250 }
1251 } else if (simulationType == SimulationType::kStabilizer) {
1252 cliffordSimulator->SaveState();
1253 for (size_t shot = 0; shot < shots; ++shot) {
1254 const auto meas = MeasureMany(qubits);
1255 ++result[meas];
1256 cliffordSimulator->RestoreState();
1257 }
1258 cliffordSimulator->ClearSavedState();
1259 } else if (simulationType == SimulationType::kTensorNetwork) {
1260 tensorNetwork->SaveState();
1261 for (size_t shot = 0; shot < shots; ++shot) {
1262 const auto meas = MeasureMany(qubits);
1263 ++result[meas];
1264 tensorNetwork->RestoreState();
1265 }
1266 tensorNetwork->ClearSavedState();
1267 } else if (simulationType == SimulationType::kPauliPropagator) {
1268 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1269 for (size_t shot = 0; shot < shots; ++shot) {
1270 const auto meas = pp->Sample(qubitsInt);
1271 ++result[meas];
1272 }
1273 } else if (simulationType == SimulationType::kPathIntegral) {
1274 if (nrQubits < 64) {
1275 if (shots > 1) {
1276 const auto &amplitudes = pathIntegralSimulator->Amplitudes();
1277 const Utils::Alias alias(amplitudes);
1278 for (size_t shot = 0; shot < shots; ++shot) {
1279 const double prob = 1. - uniformZeroOne(rng);
1280 const size_t measRaw = alias.Sample(prob);
1281 std::vector<bool> meas(qubits.size(), false);
1282 for (size_t i = 0; i < qubits.size(); ++i)
1283 if (((measRaw >> qubits[i]) & 1) == 1) meas[i] = true;
1284 ++result[meas];
1285 }
1286 } else {
1287 for (size_t shot = 0; shot < shots; ++shot) {
1288 const auto measRaw = MeasureNoCollapseMany();
1289 std::vector<bool> meas(qubits.size(), false);
1290
1291 for (size_t i = 0; i < qubits.size(); ++i)
1292 if (measRaw[qubits[i]]) meas[i] = true;
1293
1294 ++result[meas];
1295 }
1296 }
1297 } else {
1298 if (shots > 1) {
1299 const auto &amplitudes = pathIntegralSimulator->Amplitudes();
1300 const Utils::AliasBig alias(amplitudes);
1301
1302 for (size_t shot = 0; shot < shots; ++shot) {
1303 const double prob = 1. - uniformZeroOne(rng);
1304 const auto measRaw = alias.Sample(prob);
1305 std::vector<bool> meas(qubits.size(), false);
1306 for (size_t i = 0; i < qubits.size(); ++i)
1307 if (measRaw.get(qubits[i])) meas[i] = true;
1308 ++result[meas];
1309 }
1310 } else {
1311 for (size_t shot = 0; shot < shots; ++shot) {
1312 const auto measRaw = MeasureNoCollapseMany();
1313 std::vector<bool> meas(qubits.size(), false);
1314
1315 for (size_t i = 0; i < qubits.size(); ++i)
1316 if (measRaw[qubits[i]]) meas[i] = true;
1317
1318 ++result[meas];
1319 }
1320 }
1321 }
1322 } else {
1323 if (shots > 1) {
1324 const auto &statev = state->getRegisterStorage();
1325
1326 const Utils::Alias alias(statev);
1327
1328 for (size_t shot = 0; shot < shots; ++shot) {
1329 const double prob = 1. - uniformZeroOne(rng);
1330 const size_t measRaw = alias.Sample(prob);
1331
1332 std::vector<bool> meas(qubits.size(), false);
1333
1334 for (size_t i = 0; i < qubits.size(); ++i)
1335 if (((measRaw >> qubits[i]) & 1) == 1) meas[i] = true;
1336
1337 ++result[meas];
1338 }
1339 } else {
1340 for (size_t shot = 0; shot < shots; ++shot) {
1341 const auto measRaw = MeasureNoCollapseMany();
1342 std::vector<bool> meas(qubits.size(), false);
1343
1344 for (size_t i = 0; i < qubits.size(); ++i)
1345 if (measRaw[qubits[i]]) meas[i] = true;
1346
1347 ++result[meas];
1348 }
1349 }
1350 }
1351
1352 Notify();
1353 NotifyObservers(qubits);
1354
1355 return result;
1356 }
1357
1369 double ExpectationValue(const std::string &pauliStringOrig) override {
1370 if (pauliStringOrig.empty()) return 1.0;
1371
1372 std::string pauliString = pauliStringOrig;
1373 if (pauliString.size() > GetNumberOfQubits()) {
1374 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i) {
1375 const auto pauliOp = toupper(pauliString[i]);
1376 if (pauliOp != 'I' && pauliOp != 'Z') return 0.0;
1377 }
1378
1379 pauliString.resize(GetNumberOfQubits());
1380 }
1381
1382 if (simulationType == SimulationType::kStabilizer)
1383 return cliffordSimulator->ExpectationValue(pauliString);
1384 else if (simulationType == SimulationType::kTensorNetwork)
1385 return tensorNetwork->ExpectationValue(pauliString);
1386 else if (simulationType == SimulationType::kPauliPropagator)
1387 return pp->ExpectationValue(pauliString);
1388 else if (simulationType == SimulationType::kPathIntegral)
1389 return pathIntegralSimulator->ExpectationValue(pauliString);
1390
1391 // statevector or mps
1392 static const QC::Gates::PauliXGate<> xgate;
1393 static const QC::Gates::PauliYGate<> ygate;
1394 static const QC::Gates::PauliZGate<> zgate;
1395
1396 std::vector<QC::Gates::AppliedGate<Eigen::MatrixXcd>> pauliStringVec;
1397 pauliStringVec.reserve(pauliString.size());
1398
1399 for (size_t q = 0; q < pauliString.size(); ++q) {
1400 switch (toupper(pauliString[q])) {
1401 case 'X': {
1402 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1403 xgate.getRawOperatorMatrix(), static_cast<Types::qubit_t>(q));
1404 pauliStringVec.emplace_back(std::move(ag));
1405 } break;
1406 case 'Y': {
1407 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1408 ygate.getRawOperatorMatrix(), static_cast<Types::qubit_t>(q));
1409 pauliStringVec.emplace_back(std::move(ag));
1410 } break;
1411 case 'Z': {
1412 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1413 zgate.getRawOperatorMatrix(), static_cast<Types::qubit_t>(q));
1414 pauliStringVec.emplace_back(std::move(ag));
1415 } break;
1416 case 'I':
1417 [[fallthrough]];
1418 default:
1419 break;
1420 }
1421 }
1422
1423 if (pauliStringVec.empty()) return 1.0;
1424
1425 if (simulationType == SimulationType::kMatrixProductState)
1426 return mpsSimulator->ExpectationValue(pauliStringVec).real();
1427
1428 return state->ExpectationValue(pauliStringVec).real();
1429 }
1430
1438 SimulatorType GetType() const override { return SimulatorType::kQCSim; }
1439
1448 SimulationType GetSimulationType() const override { return simulationType; }
1449
1458 void Flush() override {}
1459
1469 void SaveStateToInternalDestructive() override {}
1470
1477 void RestoreInternalDestructiveSavedState() override {}
1478
1488 void SaveState() override {
1489 if (simulationType == SimulationType::kMatrixProductState)
1490 mpsSimulator->SaveState();
1491 else if (simulationType == SimulationType::kStabilizer)
1492 cliffordSimulator->SaveState();
1493 else if (simulationType == SimulationType::kTensorNetwork)
1494 tensorNetwork->SaveState();
1495 else if (simulationType == SimulationType::kPauliPropagator)
1496 pp->SaveState();
1497 else if (simulationType == SimulationType::kPathIntegral)
1498 pathIntegralSimulator->SaveState();
1499 else
1500 state->SaveState();
1501 }
1502
1511 void RestoreState() override {
1512 if (simulationType == SimulationType::kMatrixProductState)
1513 mpsSimulator->RestoreState();
1514 else if (simulationType == SimulationType::kStabilizer)
1515 cliffordSimulator->RestoreState();
1516 else if (simulationType == SimulationType::kTensorNetwork)
1517 tensorNetwork->RestoreState();
1518 else if (simulationType == SimulationType::kPauliPropagator)
1519 pp->RestoreState();
1520 else if (simulationType == SimulationType::kPathIntegral)
1521 pathIntegralSimulator->RestoreState();
1522 else
1523 state->RestoreState();
1524 }
1525
1533 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
1534 return Amplitude(outcome);
1535 }
1536
1545 void SetMultithreading(bool multithreading = true) override {
1546 enableMultithreading = multithreading;
1547 if (state) state->SetMultithreading(multithreading);
1548 if (cliffordSimulator) cliffordSimulator->SetMultithreading(multithreading);
1549 if (tensorNetwork) tensorNetwork->SetMultithreading(multithreading);
1550 if (pp) {
1551 if (multithreading)
1552 pp->EnableParallel();
1553 else
1554 pp->DisableParallel();
1555 }
1556 if (pathIntegralSimulator) {
1557 enableMultithreading = false; // not supported for now
1558 }
1559 }
1560
1568 bool GetMultithreading() const override { return enableMultithreading; }
1569
1580 bool IsQcsim() const override { return true; }
1581
1600 if (GetNumberOfQubits() > sizeof(Types::qubit_t) * 8)
1601 std::cerr
1602 << "Warning: The number of qubits to measure is larger than the "
1603 "number of bits in the Types::qubit_t type, the outcome will be "
1604 "undefined"
1605 << std::endl;
1606
1607 if (simulationType == SimulationType::kStatevector)
1608 return state->MeasureNoCollapse();
1609 else if (simulationType == SimulationType::kMatrixProductState) {
1610 const auto measured = mpsSimulator->MeasureNoCollapse();
1611 Types::qubit_t result = 0;
1612 Types::qubit_t mask = 1;
1613 for (Types::qubit_t q = 0; q < measured.size(); ++q) {
1614 if (measured.at(q)) result |= mask;
1615 mask <<= 1;
1616 }
1617 return result;
1618 } else if (simulationType == SimulationType::kPauliPropagator) {
1619 std::vector<int> qubitsInt(GetNumberOfQubits());
1620 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
1621 const auto res = pp->Sample(qubitsInt);
1622 Types::qubit_t result = 0;
1623 for (size_t i = 0; i < res.size(); ++i) {
1624 if (res[i]) result |= (1ULL << i);
1625 }
1626 return result;
1627 } else if (simulationType == SimulationType::kPathIntegral) {
1628 if (nrQubits < 64) {
1629 const auto measured = pathIntegralSimulator->MeasureNoCollapse();
1630 Types::qubit_t result = 0;
1631 Types::qubit_t mask = 1;
1632 for (Types::qubit_t q = 0; q < measured.size(); ++q) {
1633 if (measured.get(q)) result |= mask;
1634 mask <<= 1;
1635 }
1636 return result;
1637 } else {
1638 throw std::runtime_error(
1639 "QCSimState::MeasureNoCollapse: The path integral simulator does not "
1640 "support measuring more than 63 qubits into 64 bits integers.");
1641 }
1642 }
1643
1644 throw std::runtime_error(
1645 "QCSimState::MeasureNoCollapse: Invalid simulation type for "
1646 "measuring "
1647 "all the qubits without collapsing the state.");
1648
1649 return 0;
1650 }
1651
1667 std::vector<bool> MeasureNoCollapseMany() override {
1668 if (simulationType == SimulationType::kStatevector) {
1669 auto state = MeasureNoCollapse();
1670 std::vector<bool> res(nrQubits);
1671 for (size_t i = 0; i < nrQubits; ++i) res[i] = ((state >> i) & 1) == 1;
1672 return res;
1673 } else if (simulationType == SimulationType::kMatrixProductState) {
1674 const auto measured = mpsSimulator->MeasureNoCollapse();
1675 std::vector<bool> res(nrQubits);
1676 for (size_t i = 0; i < nrQubits; ++i) res[i] = measured.at(i);
1677 return res;
1678 } else if (simulationType == SimulationType::kPauliPropagator) {
1679 std::vector<int> qubitsInt(GetNumberOfQubits());
1680 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
1681 return pp->Sample(qubitsInt);
1682 } else if (simulationType == SimulationType::kPathIntegral) {
1683 const auto measured = pathIntegralSimulator->MeasureNoCollapse();
1684 std::vector<bool> res(nrQubits);
1685 for (size_t i = 0; i < nrQubits; ++i) res[i] = measured.get(i);
1686 return res;
1687 }
1688
1689 throw std::runtime_error(
1690 "QCSimState::MeasureNoCollapseMany: Invalid simulation type for "
1691 "measuring all the qubits without collapsing the state.");
1692
1693 return {};
1694 }
1695
1696 protected:
1697 SimulationType simulationType =
1698 SimulationType::kStatevector;
1699
1700 std::unique_ptr<QC::QubitRegister<>> state;
1701 std::unique_ptr<QC::TensorNetworks::MPSSimulator>
1702 mpsSimulator;
1703 std::unique_ptr<QC::Clifford::StabilizerSimulator>
1704 cliffordSimulator;
1705 std::unique_ptr<TensorNetworks::TensorNetwork>
1706 tensorNetwork;
1707 std::unique_ptr<QcsimPauliPropagator> pp;
1708 std::unique_ptr<PathIntegralSimulator>
1709 pathIntegralSimulator;
1710
1711 size_t nrQubits = 0;
1712 bool limitSize = false;
1713 bool limitEntanglement = false;
1714 Eigen::Index chi = 10; // if limitSize is true
1715 double singularValueThreshold = 0.; // if limitEntanglement is true
1716 bool enableMultithreading = true;
1717 bool useMPSMeasureNoCollapse =
1718 true;
1719
1720 // PauliPropagator truncation settings
1721 double ppCoefficientThreshold = 0.;
1722 size_t ppPauliWeightThreshold = std::numeric_limits<size_t>::max();
1723 int ppStepsBetweenTrims = std::numeric_limits<int>::max();
1724
1725 int lookaheadDepth = 0;
1726 int lookaheadDepthWithHeuristic = 0;
1727 bool useOptimalMeetingPosition = true;
1728 std::vector<std::shared_ptr<Circuits::IOperation<>>> upcomingGates;
1729 long long int upcomingGateIndex = 0;
1730 double growthFactorSwap = 1.;
1731 double growthFactorGate = 0.65;
1732
1733 std::unique_ptr<Simulators::MPSDummySimulator> dummySim;
1734
1735 // Observer that counts applied gates to track position in upcomingGates
1736 class GateCounterObserver : public ISimulatorObserver {
1737 public:
1738 GateCounterObserver(long long int &indexRef) : index(indexRef) {}
1739 void Update(const Types::qubits_vector &) override { ++index; }
1740
1741 private:
1742 long long int &index;
1743 };
1744 std::shared_ptr<GateCounterObserver> gateCounterObserver;
1745
1746 std::mt19937_64 rng;
1747 std::uniform_real_distribution<double> uniformZeroOne;
1748};
1749
1750} // namespace Private
1751} // namespace Simulators
1752
1753#endif
1754
1755#endif // !_QCSIMSTATE_H_
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)
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)
int IsQcsim(void *sim)
int SaveState(void *sim)
The operation interface.
Definition Operations.h:358
QC::PathIntegral::FastVectorBool Sample(double v) const
Definition Alias.h:248
size_t Sample(double v) const
Definition Alias.h:199
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:22
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:21