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 } else if (simulationType == SimulationType::kPathIntegral) {
94 pathIntegralSimulator = std::make_unique<PathIntegralSimulator>();
95 pathIntegralSimulator->SetStartZeroState(nrQubits);
96 } else
97 state = std::make_unique<QC::QubitRegister<>>(nrQubits);
98
99 SetMultithreading(enableMultithreading);
100 }
101 }
102
114 void InitializeState(size_t num_qubits,
115 std::vector<std::complex<double>> &amplitudes) override {
116 if (num_qubits == 0) return;
117 Clear();
118 nrQubits = num_qubits;
119 Initialize();
120 if (simulationType != SimulationType::kStatevector)
121 throw std::runtime_error(
122 "QCSimState::InitializeState: Invalid "
123 "simulation type for initializing the state.");
124
125 Eigen::VectorXcd amplitudesEigen(
126 Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(),
127 amplitudes.size()));
128 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
129 }
130
142 /*
143 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
144 avoid_init_allocator<std::complex<double>>>& amplitudes) override
145 {
146 Clear();
147 nrQubits = num_qubits;
148 Initialize();
149 Eigen::VectorXcd amplitudesEigen(Eigen::Map<Eigen::VectorXcd,
150 Eigen::Unaligned>(amplitudes.data(), amplitudes.size()));
151 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
152 }
153 */
154
166#ifndef NO_QISKIT_AER
167 void InitializeState(size_t num_qubits,
168 AER::Vector<std::complex<double>> &amplitudes) override {
169 if (num_qubits == 0) return;
170 Clear();
171 nrQubits = num_qubits;
172 Initialize();
173 if (simulationType != SimulationType::kStatevector)
174 throw std::runtime_error(
175 "QCSimState::InitializeState: Invalid "
176 "simulation type for initializing the state.");
177
178 Eigen::VectorXcd amplitudesEigen(
179 Eigen::Map<Eigen::VectorXcd, Eigen::Unaligned>(amplitudes.data(),
180 amplitudes.size()));
181 state->setRegisterStorageFastNoNormalize(amplitudesEigen);
182 }
183#endif
184
196 void InitializeState(size_t num_qubits,
197 Eigen::VectorXcd &amplitudes) override {
198 if (num_qubits == 0) return;
199 Clear();
200 nrQubits = num_qubits;
201 Initialize();
202
203 if (simulationType != SimulationType::kStatevector)
204 throw std::runtime_error(
205 "QCSimState::InitializeState: Invalid "
206 "simulation type for initializing the state.");
207
208 state = std::make_unique<QC::QubitRegister<>>(nrQubits, amplitudes);
209 state->SetMultithreading(enableMultithreading);
210 }
211
218 void Reset() override {
219 if (mpsSimulator)
220 mpsSimulator->Clear();
221 else if (cliffordSimulator)
222 cliffordSimulator->Reset();
223 else if (tensorNetwork)
224 tensorNetwork->Clear();
225 else if (state)
226 state->Reset();
227 else if (pp)
228 pp->ClearOperations();
229 else if (pathIntegralSimulator) {
230 pathIntegralSimulator->Reset();
231 pathIntegralSimulator->SetStartZeroState(nrQubits);
232 }
233
234 upcomingGateIndex = 0;
235 }
236
244 bool SupportsMPSSwapOptimization() const override { return true; }
245
254 void SetInitialQubitsMap(
255 const std::vector<long long int> &initialMap) override {
256 if (mpsSimulator) {
257 mpsSimulator->SetInitialQubitsMap(initialMap);
258 if (!dummySim || dummySim->getNrQubits() != initialMap.size()) {
259 dummySim =
260 std::make_unique<Simulators::MPSDummySimulator>(initialMap.size());
261 dummySim->SetMaxBondDimension(
262 limitSize ? static_cast<long long int>(chi) : 0);
263 }
264 dummySim->setGrowthFactorGate(growthFactorGate);
265 dummySim->setGrowthFactorSwap(growthFactorSwap);
266 dummySim->SetInitialQubitsMap(initialMap);
267 }
268 }
269
270 void SetUseOptimalMeetingPosition(bool enable) override {
271 useOptimalMeetingPosition = enable;
272 if (mpsSimulator) mpsSimulator->SetUseOptimalMeetingPosition(enable);
273 }
274
275 void SetLookaheadDepth(int depth) override {
276 lookaheadDepth = depth;
277 if (mpsSimulator && depth > 0 && !useOptimalMeetingPosition)
278 mpsSimulator->SetUseOptimalMeetingPosition(true);
279 }
280
281 void SetLookaheadDepthWithHeuristic(int depth) override {
282 lookaheadDepthWithHeuristic = depth;
283 if (lookaheadDepth < depth) SetLookaheadDepth(depth);
284 }
285
286 void SetUpcomingGates(
287 const std::vector<std::shared_ptr<Circuits::IOperation<double>>> &gates)
288 override {
289 upcomingGates = gates;
290 upcomingGateIndex = 0;
291
292 if (!mpsSimulator || lookaheadDepth <= 0) return;
293
294 // Register an observer that advances the gate index
295 ClearObservers(); // for now we only have this observer, so this should be
296 // fine
297 gateCounterObserver =
298 std::make_shared<GateCounterObserver>(upcomingGateIndex);
299 RegisterObserver(gateCounterObserver);
300
301 // Set up a meeting position callback that uses MPSDummySimulator
302 // for lookahead evaluation with actual bond dimensions
303 // the callback is called only for two qubits gates and only if executing
304 // them would require a swap
305 mpsSimulator->SetMeetingPositionCallback(
306 [this](/*const auto &qMap,*/ const auto &bondDims)
307 -> QC::TensorNetworks::MPSSimulatorInterface::IndexType {
308 if (upcomingGates.empty() ||
309 upcomingGateIndex >= upcomingGates.size()) {
310 return -1; // will fallback to default behavior
311 }
312
313 const size_t nQ = bondDims.size() + 1;
314
315 if (!dummySim || dummySim->getNrQubits() != nQ) {
316 dummySim = std::make_unique<Simulators::MPSDummySimulator>(nQ);
317 dummySim->SetMaxBondDimension(
318 limitSize ? static_cast<long long int>(chi) : 0);
319 dummySim->setGrowthFactorGate(growthFactorGate);
320 dummySim->setGrowthFactorSwap(growthFactorSwap);
321 }
322
323 // Seed dummy with current real simulator state
324 // std::vector<long long int> map64(qMap.begin(), qMap.end());
325 // dummySim->SetInitialQubitsMap(map64);
326 dummySim->setTotalSwappingCost(0);
327
328 // check qubits map:
329 /*
330 auto qbitmMap = dummySim->getQubitsMap();
331 for (size_t i = 0; i < nQ; ++i) {
332 if (qbitmMap[i] != qMap[i]) {
333 std::cerr << "Error: qubits map mismatch at index " << i
334 << ": dummySim has " << qbitmMap[i]
335 << " but real sim has " << qMap[i] << std::endl;
336 exit(0);
337 }
338 }
339 */
340
341 // check them, they should be the same, otherwise something is wrong
342
343 // Convert actual bond dims to doubles
344 std::vector<double> bondDimsD(bondDims.begin(), bondDims.end());
345 dummySim->SetCurrentBondDimensions(bondDimsD);
346
347 // display bond dimensions for debugging
348 /*
349 std::cout << "Bond dimensions before swapping and applying the gate:
350 "; for (size_t i = 0; i < bondDims.size(); ++i) { std::cout <<
351 bondDims[i] << " ";
352 }
353 std::cout << std::endl;
354 */
355
356 const auto &op = upcomingGates[upcomingGateIndex];
357 const auto qbits = op->AffectedQubits();
358
359 if (qbits.size() != 2) {
360 std::cerr << "Error: Meeting position callback called for a gate "
361 "that does not have exactly 2 qubits."
362 << std::endl;
363
364 return -1; // will fallback
365 }
366
367 /*
368 const auto &qmap = dummySim->getQubitsMap();
369
370 std::cout << "Applying 2-qubit gate on physical qubits " <<
371 qmap[qbits[0]] << " and "
372 << qmap[qbits[1]]
373 << std::endl;
374 */
375 /*
376 std::cout << "Finding best meeting position for upcoming gates
377 starting at index "
378 << upcomingGateIndex << " with lookahead depth " <<
379 lookaheadDepth << " and heuristic depth "
380 << lookaheadDepthWithHeuristic << std::endl;
381
382
383
384 std::cout << "Affected qubits: ";
385 for (const auto &q : qbits) std::cout << q << " ";
386 std::cout << std::endl;
387
388 std::cout << "Current qubits map: ";
389 for (size_t i = 0; i < qMap.size(); ++i) std::cout << qMap[i] << " ";
390 std::cout << std::endl;
391
392 std::cout << "Current inverse qubits map: ";
393 for (size_t i = 0; i < qMapInv.size(); ++i) std::cout << qMapInv[i] <<
394 " "; std::cout << std::endl;
395 */
396
397 double bestCost = std::numeric_limits<double>::infinity();
398 auto res = dummySim->FindBestMeetingPosition(
399 upcomingGates, upcomingGateIndex, lookaheadDepth,
400 lookaheadDepthWithHeuristic, 0, bestCost);
401
402 // std::cout << "Swapping the two qubits on position: " << res << "
403 // and " << (res + 1) << std::endl;
404
405 dummySim->SwapQubitsToPosition(qbits[0], qbits[1], res);
406 dummySim->ApplyGate(op);
407
408 // display the expected bond dimensions after applying the gate for
409 // debugging
410
411 /*
412 const auto &expectedBondDims = dummySim->getCurrentBondDimensions();
413 std::cout << "Expected bond dimensions after swapping and applying "
414 "the gate: ";
415 for (size_t i = 0; i < expectedBondDims.size(); ++i) {
416 std::cout << expectedBondDims[i] << " ";
417 }
418 std::cout << std::endl;
419 */
420
421 // std::cout << "Best meeting position: " << res
422 // << " with estimated cost: " << bestCost << std::endl;
423
424 return res;
425 });
426 }
427
436 long long int GetGatesCounter() const override { return upcomingGateIndex; }
437
447 void SetGatesCounter(long long int counter) override {
448 upcomingGateIndex = counter;
449 }
450
459 void IncrementGatesCounter() override { ++upcomingGateIndex; }
460
461 double getGrowthFactorSwap() const override { return growthFactorSwap; }
462 double getGrowthFactorGate() const override { return growthFactorGate; }
463
464 void setGrowthFactorSwap(double factor) override {
465 growthFactorSwap = factor;
466 if (dummySim) dummySim->setGrowthFactorSwap(factor);
467 }
468
469 void setGrowthFactorGate(double factor) override {
470 growthFactorGate = factor;
471 if (dummySim) dummySim->setGrowthFactorGate(factor);
472 }
473
482 void Configure(const char *key, const char *value) override {
483 if (std::string("method") == key) {
484 if (std::string("statevector") == value)
485 simulationType = SimulationType::kStatevector;
486 else if (std::string("matrix_product_state") == value)
487 simulationType = SimulationType::kMatrixProductState;
488 else if (std::string("stabilizer") == value)
489 simulationType = SimulationType::kStabilizer;
490 else if (std::string("tensor_network") == value)
491 simulationType = SimulationType::kTensorNetwork;
492 else if (std::string("pauli_propagator") == value)
493 simulationType = SimulationType::kPauliPropagator;
494 else if (std::string("path_integral") == value)
495 simulationType = SimulationType::kPathIntegral;
496 } else if (std::string("matrix_product_state_truncation_threshold") ==
497 key) {
498 singularValueThreshold = std::stod(value);
499 if (singularValueThreshold > 0.) {
500 limitEntanglement = true;
501 if (mpsSimulator)
502 mpsSimulator->setLimitEntanglement(singularValueThreshold);
503 } else
504 limitEntanglement = false;
505 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
506 chi = std::stoi(value);
507 if (chi > 0) {
508 limitSize = true;
509 if (mpsSimulator) mpsSimulator->setLimitBondDimension(chi);
510 if (dummySim)
511 dummySim->SetMaxBondDimension(static_cast<long long int>(chi));
512 } else {
513 limitSize = false;
514 if (mpsSimulator) mpsSimulator->setLimitBondDimension(0);
515 if (dummySim) dummySim->SetMaxBondDimension(0);
516 }
517 } else if (std::string("mps_sample_measure_algorithm") == key)
518 useMPSMeasureNoCollapse = std::string("mps_probabilities") == value;
519 }
520
528 std::string GetConfiguration(const char *key) const override {
529 if (std::string("method") == key) {
530 switch (simulationType) {
531 case SimulationType::kStatevector:
532 return "statevector";
533 case SimulationType::kMatrixProductState:
534 return "matrix_product_state";
535 case SimulationType::kStabilizer:
536 return "stabilizer";
537 case SimulationType::kTensorNetwork:
538 return "tensor_network";
539 case SimulationType::kPauliPropagator:
540 return "pauli_propagator";
541 case SimulationType::kPathIntegral:
542 return "path_integral";
543 default:
544 return "other";
545 }
546 } else if (std::string("matrix_product_state_truncation_threshold") ==
547 key) {
548 if (limitEntanglement && singularValueThreshold > 0.) {
549 std::ostringstream oss;
550 oss << std::setprecision(std::numeric_limits<double>::max_digits10)
551 << singularValueThreshold;
552 return oss.str();
553 }
554 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
555 if (limitSize && chi > 0) return std::to_string(chi);
556 } else if (std::string("mps_sample_measure_algorithm") == key) {
557 return useMPSMeasureNoCollapse ? "mps_probabilities"
558 : "mps_apply_measure";
559 }
560
561 return "";
562 }
563
571 size_t AllocateQubits(size_t num_qubits) override {
572 if ((simulationType == SimulationType::kStatevector && state) ||
573 (simulationType == SimulationType::kMatrixProductState &&
574 mpsSimulator) ||
575 (simulationType == SimulationType::kStabilizer && cliffordSimulator) ||
576 (simulationType == SimulationType::kTensorNetwork && tensorNetwork))
577 return 0;
578
579 const size_t oldNrQubits = nrQubits;
580 nrQubits += num_qubits;
581 if (simulationType == SimulationType::kPauliPropagator)
582 if (pp) pp->SetNrQubits(static_cast<int>(nrQubits));
583
584 return oldNrQubits;
585 }
586
593 size_t GetNumberOfQubits() const override { return nrQubits; }
594
602 void Clear() override {
603 state = nullptr;
604 mpsSimulator = nullptr;
605 cliffordSimulator = nullptr;
606 tensorNetwork = nullptr;
607 pp = nullptr;
608 pathIntegralSimulator = nullptr;
609 dummySim = nullptr;
610 nrQubits = 0;
611 upcomingGateIndex = 0;
612 upcomingGates.clear();
613 }
614
625 size_t Measure(const Types::qubits_vector &qubits) override {
626 // TODO: this is inefficient, maybe implement it better in qcsim
627 // for now it has the possibility of measuring a qubits interval, but not a
628 // list of qubits
629 if (qubits.size() > sizeof(size_t) * 8)
630 std::cerr
631 << "Warning: The number of qubits to measure is larger than the "
632 "number of bits in the size_t type, the outcome will be undefined"
633 << std::endl;
634
635 size_t res = 0;
636 size_t mask = 1ULL;
637
638 DontNotify();
639 if (simulationType == SimulationType::kStatevector) {
640 for (size_t qubit : qubits) {
641 if (state->MeasureQubit(static_cast<unsigned int>(qubit))) res |= mask;
642 mask <<= 1;
643 }
644 } else if (simulationType == SimulationType::kStabilizer) {
645 for (size_t qubit : qubits) {
646 if (cliffordSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
647 res |= mask;
648 mask <<= 1;
649 }
650 } else if (simulationType == SimulationType::kTensorNetwork) {
651 for (size_t qubit : qubits) {
652 if (tensorNetwork->Measure(static_cast<unsigned int>(qubit)))
653 res |= mask;
654 mask <<= 1;
655 }
656 } else if (simulationType == SimulationType::kPauliPropagator) {
657 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
658 const auto res = pp->Measure(qubitsInt);
659 Types::qubit_t result = 0;
660 for (size_t i = 0; i < res.size(); ++i) {
661 if (res[i]) result |= mask;
662 mask <<= 1;
663 }
664 return result;
665 } else if (simulationType == SimulationType::kPathIntegral) {
666 for (size_t qubit : qubits) {
667 if (pathIntegralSimulator->MeasureQubit(qubit)) res |= mask;
668 mask <<= 1;
669 }
670 } else {
671 /*
672 for (size_t qubit : qubits)
673 {
674 if (mpsSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
675 res |= mask;
676 mask <<= 1;
677 }
678 */
679 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
680 auto measured = mpsSimulator->MeasureQubits(qubitsSet);
681 for (Types::qubit_t qubit : qubits) {
682 if (measured[qubit]) res |= mask;
683 mask <<= 1;
684 }
685 }
686 Notify();
687
688 NotifyObservers(qubits);
689
690 return res;
691 }
692
699 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
700 std::vector<bool> res(qubits.size(), false);
701 DontNotify();
702
703 if (simulationType == SimulationType::kStatevector) {
704 for (size_t q = 0; q < qubits.size(); ++q)
705 if (state->MeasureQubit(static_cast<unsigned int>(qubits[q])))
706 res[q] = true;
707 } else if (simulationType == SimulationType::kStabilizer) {
708 for (size_t q = 0; q < qubits.size(); ++q)
709 if (cliffordSimulator->MeasureQubit(
710 static_cast<unsigned int>(qubits[q])))
711 res[q] = true;
712 } else if (simulationType == SimulationType::kTensorNetwork) {
713 for (size_t q = 0; q < qubits.size(); ++q)
714 if (tensorNetwork->Measure(static_cast<unsigned int>(qubits[q])))
715 res[q] = true;
716 } else if (simulationType == SimulationType::kPauliPropagator) {
717 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
718 res = pp->Measure(qubitsInt);
719 } else if (simulationType == SimulationType::kPathIntegral) {
720 for (size_t q = 0; q < qubits.size(); ++q)
721 if (pathIntegralSimulator->MeasureQubit(qubits[q])) res[q] = true;
722 } else {
723 const std::set<Eigen::Index> qubitsSet(qubits.begin(), qubits.end());
724 auto measured = mpsSimulator->MeasureQubits(qubitsSet);
725 for (size_t q = 0; q < qubits.size(); ++q)
726 if (measured[qubits[q]]) res[q] = true;
727 }
728 Notify();
729 NotifyObservers(qubits);
730
731 return res;
732 }
733
740 void ApplyReset(const Types::qubits_vector &qubits) override {
741 QC::Gates::PauliXGate xGate;
742
743 DontNotify();
744 if (simulationType == SimulationType::kStatevector) {
745 for (size_t qubit : qubits)
746 if (state->MeasureQubit(static_cast<unsigned int>(qubit)))
747 state->ApplyGate(xGate, static_cast<unsigned int>(qubit));
748 } else if (simulationType == SimulationType::kStabilizer) {
749 for (size_t qubit : qubits)
750 if (cliffordSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
751 cliffordSimulator->ApplyX(static_cast<unsigned int>(qubit));
752 } else if (simulationType == SimulationType::kTensorNetwork) {
753 for (size_t qubit : qubits)
754 if (tensorNetwork->Measure(static_cast<unsigned int>(qubit)))
755 tensorNetwork->AddGate(xGate, static_cast<unsigned int>(qubit));
756 } else if (simulationType == SimulationType::kPauliPropagator) {
757 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
758 const auto res = pp->Measure(qubitsInt);
759 for (size_t i = 0; i < res.size(); ++i) {
760 if (res[i]) pp->ApplyX(qubitsInt[i]);
761 }
762 } else if (simulationType == SimulationType::kPathIntegral) {
763 for (size_t qubit : qubits)
764 if (pathIntegralSimulator->MeasureQubit(qubit)) {
765 QC::Gates::AppliedGate<> gate(xGate.getRawOperatorMatrix(), qubit);
766 pathIntegralSimulator->PropagateStep(
767 gate, pathIntegralSimulator->Amplitudes());
768 }
769 } else {
770 for (size_t qubit : qubits)
771 if (mpsSimulator->MeasureQubit(static_cast<unsigned int>(qubit)))
772 mpsSimulator->ApplyGate(xGate, static_cast<unsigned int>(qubit));
773 }
774 Notify();
775
776 NotifyObservers(qubits);
777 }
778
790 double Probability(Types::qubit_t outcome) override {
791 if (simulationType == SimulationType::kMatrixProductState)
792 return mpsSimulator->getBasisStateProbability(
793 static_cast<unsigned int>(outcome));
794 else if (simulationType == SimulationType::kStabilizer)
795 return cliffordSimulator->getBasisStateProbability(
796 static_cast<unsigned int>(outcome));
797 else if (simulationType == SimulationType::kTensorNetwork)
798 return tensorNetwork->getBasisStateProbability(outcome);
799 else if (simulationType == SimulationType::kPauliPropagator)
800 return pp->Probability(outcome);
801 else if (simulationType == SimulationType::kPathIntegral)
802 return pathIntegralSimulator->Probability(outcome);
803
804 return state->getBasisStateProbability(static_cast<unsigned int>(outcome));
805 }
806
817 std::complex<double> Amplitude(Types::qubit_t outcome) override {
818 if (simulationType == SimulationType::kMatrixProductState)
819 return mpsSimulator->getBasisStateAmplitude(
820 static_cast<unsigned int>(outcome));
821 else if (simulationType == SimulationType::kPathIntegral)
822 return pathIntegralSimulator->AmplitudeForOutcome(outcome);
823 else if (simulationType == SimulationType::kStabilizer)
824 throw std::runtime_error(
825 "QCSimState::Amplitude: Invalid simulation type for obtaining the "
826 "amplitude of the specified outcome.");
827 else if (simulationType == SimulationType::kTensorNetwork)
828 throw std::runtime_error(
829 "QCSimState::Amplitude: Not supported for the "
830 "tensor network simulator.");
831 else if (simulationType == SimulationType::kPauliPropagator)
832 throw std::runtime_error(
833 "QCSimState::Amplitude: Invalid simulation type for obtaining the "
834 "amplitude of the specified outcome.");
835
836 return state->getBasisStateAmplitude(static_cast<unsigned int>(outcome));
837 }
838
852 std::complex<double> ProjectOnZero() override {
853 if (simulationType == SimulationType::kMatrixProductState)
854 return mpsSimulator->ProjectOnZero();
855
856 return Amplitude(0);
857 }
858
869 std::vector<double> AllProbabilities() override {
870 // TODO: In principle this could be done, but why? It should be costly.
871 if (simulationType == SimulationType::kTensorNetwork)
872 throw std::runtime_error(
873 "QCSimState::AllProbabilities: Invalid "
874 "simulation type for obtaining probabilities.");
875 else if (simulationType == SimulationType::kStabilizer)
876 return cliffordSimulator->AllProbabilities();
877 else if (simulationType == SimulationType::kPauliPropagator) {
878 const size_t nrBasisStates = 1ULL << GetNumberOfQubits();
879 std::vector<double> result(nrBasisStates);
880 for (size_t i = 0; i < nrBasisStates; ++i) result[i] = pp->Probability(i);
881 return result;
882 } else if (simulationType == SimulationType::kPathIntegral) {
883 const size_t nrBasisStates = 1ULL << GetNumberOfQubits();
884 std::vector<double> result(nrBasisStates);
885 for (size_t i = 0; i < nrBasisStates; ++i)
886 result[i] = pathIntegralSimulator->Probability(i);
887 return result;
888 }
889
890 const Eigen::VectorXcd probs =
891 simulationType == SimulationType::kMatrixProductState
892 ? mpsSimulator->getRegisterStorage().cwiseAbs2()
893 : state->getRegisterStorage().cwiseAbs2();
894
895 std::vector<double> result(probs.size());
896
897 for (int i = 0; i < probs.size(); ++i) result[i] = probs[i].real();
898
899 return result;
900 }
901
913 std::vector<double> Probabilities(
914 const Types::qubits_vector &qubits) override {
915 if (simulationType == SimulationType::kStabilizer)
916 throw std::runtime_error(
917 "QCSimState::Probabilities: Invalid simulation "
918 "type for obtaining probabilities.");
919 else if (simulationType == SimulationType::kTensorNetwork) {
920 // TODO: Implement this!!!
921 throw std::runtime_error(
922 "QCSimState::Probabilities: Not implemented yet "
923 "for the tensor network simulator.");
924 }
925
926 std::vector<double> result(qubits.size());
927
928 if (simulationType == SimulationType::kMatrixProductState) {
929 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
930 result[i] = mpsSimulator->getBasisStateProbability(qubits[i]);
931 } else if (simulationType == SimulationType::kPauliPropagator) {
932 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
933 result[i] = pp->Probability(qubits[i]);
934 } else if (simulationType == SimulationType::kPathIntegral) {
935 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
936 result[i] = pathIntegralSimulator->Probability(qubits[i]);
937 } else {
938 const Eigen::VectorXcd &reg = state->getRegisterStorage();
939
940 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
941 result[i] = std::norm(reg[qubits[i]]);
942 }
943
944 return result;
945 }
946
963 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
964 const Types::qubits_vector &qubits, size_t shots = 1000) override {
965 if (qubits.empty() || shots == 0) return {};
966
967 if (qubits.size() > sizeof(size_t) * 8)
968 std::cerr
969 << "Warning: The number of qubits to measure is larger than the "
970 "number of bits in the size_t type, the outcome will be undefined"
971 << std::endl;
972
973 // TODO: this is inefficient, maybe implement it better in qcsim
974 // for now it has the possibility of measuring a qubits interval, but not a
975 // list of qubits
976 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
977
978 DontNotify();
979
980 if (simulationType == SimulationType::kMatrixProductState) {
981 bool normal = true;
982 if (useMPSMeasureNoCollapse) {
983 // check to see if it can be used
984 const std::set<Eigen::Index> qset(qubits.begin(), qubits.end());
985 if (qset.size() == GetNumberOfQubits()) {
986 // it can!
987 normal = false;
988 for (size_t shot = 0; shot < shots; ++shot) {
989 const size_t measRaw = MeasureNoCollapse();
990 size_t meas = 0;
991 size_t mask = 1ULL;
992
993 // translate the measurement
994 for (auto q : qubits) {
995 const size_t qubitMask = 1ULL << q;
996 if (measRaw & qubitMask) meas |= mask;
997 mask <<= 1ULL;
998 }
999
1000 ++result[meas];
1001 }
1002 } else if (qset.size() > 1) {
1003 mpsSimulator->MoveAtBeginningOfChain(qset);
1004 // now sample
1005 normal = false;
1006 for (size_t shot = 0; shot < shots; ++shot) {
1007 const auto measRaw = mpsSimulator->MeasureNoCollapse(qset);
1008 size_t meas = 0;
1009 size_t mask = 1ULL;
1010
1011 // might not be in the requested order
1012 // translate the measurement
1013 for (auto q : qubits) {
1014 if (measRaw.at(q)) meas |= mask;
1015 mask <<= 1ULL;
1016 }
1017
1018 ++result[meas];
1019 }
1020
1021 } else if (qset.size() == 1) {
1022 // if only one qubit is measured, we can use the probability
1023 normal = false;
1024 const auto prob0 = mpsSimulator->GetProbability(qubits[0]);
1025 for (size_t shot = 0; shot < shots; ++shot) {
1026 const size_t meas = uniformZeroOne(rng) < prob0 ? 0ULL : 1ULL;
1027 size_t m = meas;
1028 // why would somebody set more than one time?
1029 for (size_t i = 1; i < qubits.size(); ++i) {
1030 m <<= 1ULL;
1031 m |= meas;
1032 }
1033 ++result[m];
1034 }
1035 }
1036 }
1037
1038 if (normal) {
1039 auto savedState = mpsSimulator->getState();
1040 for (size_t shot = 0; shot < shots; ++shot) {
1041 const size_t meas = Measure(qubits);
1042 ++result[meas];
1043 mpsSimulator->setState(savedState);
1044 }
1045 }
1046 } else if (simulationType == SimulationType::kStabilizer) {
1047 cliffordSimulator->SaveState();
1048 for (size_t shot = 0; shot < shots; ++shot) {
1049 const size_t meas = Measure(qubits);
1050 ++result[meas];
1051 cliffordSimulator->RestoreState();
1052 }
1053 cliffordSimulator->ClearSavedState();
1054 } else if (simulationType == SimulationType::kTensorNetwork) {
1055 tensorNetwork->SaveState();
1056 for (size_t shot = 0; shot < shots; ++shot) {
1057 const size_t meas = Measure(qubits);
1058 ++result[meas];
1059 tensorNetwork->RestoreState();
1060 }
1061 tensorNetwork->ClearSavedState();
1062 } else if (simulationType == SimulationType::kPauliPropagator) {
1063 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1064 for (size_t shot = 0; shot < shots; ++shot) {
1065 const auto res = pp->Sample(qubitsInt);
1066
1067 size_t meas = 0;
1068 for (size_t i = 0; i < qubits.size(); ++i) {
1069 if (res[i]) meas |= (1ULL << i);
1070 }
1071
1072 ++result[meas];
1073 }
1074 } else if (simulationType == SimulationType::kPathIntegral) {
1075 if (nrQubits < 64) {
1076 if (shots > 1) {
1077 const auto &amplitudes = pathIntegralSimulator->Amplitudes();
1078 const Utils::Alias alias(amplitudes);
1079
1080 for (size_t shot = 0; shot < shots; ++shot) {
1081 const double prob = 1. - uniformZeroOne(rng);
1082 const size_t measRaw = alias.Sample(prob);
1083
1084 size_t meas = 0;
1085 size_t mask = 1ULL;
1086 for (auto q : qubits) {
1087 const size_t qubitMask = 1ULL << q;
1088 if ((measRaw & qubitMask) != 0) meas |= mask;
1089 mask <<= 1ULL;
1090 }
1091
1092 ++result[meas];
1093 }
1094 } else {
1095 const size_t measRaw = MeasureNoCollapse();
1096 size_t meas = 0;
1097 size_t mask = 1ULL;
1098 for (auto q : qubits) {
1099 const size_t qubitMask = 1ULL << q;
1100 if ((measRaw & qubitMask) != 0) meas |= mask;
1101 mask <<= 1ULL;
1102 }
1103 ++result[meas];
1104 }
1105 } else {
1106 throw std::runtime_error(
1107 "QCSimState::SampleCounts: The path integral simulator does not "
1108 "support sampling for more than 63 qubits into 64 bits integers.");
1109 }
1110 } else {
1111 if (shots > 1) {
1112 const auto &statev = state->getRegisterStorage();
1113
1114 const Utils::Alias alias(statev);
1115
1116 for (size_t shot = 0; shot < shots; ++shot) {
1117 const double prob = 1. - uniformZeroOne(rng);
1118 const size_t measRaw = alias.Sample(prob);
1119
1120 size_t meas = 0;
1121 size_t mask = 1ULL;
1122 for (auto q : qubits) {
1123 const size_t qubitMask = 1ULL << q;
1124 if ((measRaw & qubitMask) != 0) meas |= mask;
1125 mask <<= 1ULL;
1126 }
1127
1128 ++result[meas];
1129 }
1130 } else {
1131 for (size_t shot = 0; shot < shots; ++shot) {
1132 const size_t measRaw = MeasureNoCollapse();
1133 size_t meas = 0;
1134 size_t mask = 1ULL;
1135
1136 for (auto q : qubits) {
1137 const size_t qubitMask = 1ULL << q;
1138 if ((measRaw & qubitMask) != 0) meas |= mask;
1139 mask <<= 1ULL;
1140 }
1141
1142 ++result[meas];
1143 }
1144 }
1145 }
1146
1147 Notify();
1148 NotifyObservers(qubits);
1149
1150 return result;
1151 }
1152
1166 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
1167 const Types::qubits_vector &qubits, size_t shots = 1000) override {
1168 if (qubits.empty() || shots == 0) return {};
1169
1170 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
1171
1172 DontNotify();
1173
1174 if (simulationType == SimulationType::kMatrixProductState) {
1175 bool normal = true;
1176 if (useMPSMeasureNoCollapse) {
1177 // check to see if it can be used
1178 const std::set<Eigen::Index> qset(qubits.begin(), qubits.end());
1179 if (qset.size() == GetNumberOfQubits()) {
1180 // it can!
1181 normal = false;
1182 for (size_t shot = 0; shot < shots; ++shot) {
1183 const auto meas = MeasureNoCollapseMany();
1184
1185 // might not be in the requested order
1186 // translate the measurement
1187 std::vector<bool> measVec(qubits.size());
1188 for (size_t i = 0; i < qubits.size(); ++i)
1189 measVec[i] = meas[qubits[i]];
1190
1191 ++result[measVec];
1192 }
1193 } else if (qset.size() > 1) {
1194 mpsSimulator->MoveAtBeginningOfChain(qset);
1195 // now sample
1196 normal = false;
1197 for (size_t shot = 0; shot < shots; ++shot) {
1198 const auto meas = mpsSimulator->MeasureNoCollapse(qset);
1199
1200 // might not be in the requested order
1201 // translate the measurement
1202 std::vector<bool> measVec(qubits.size());
1203 for (size_t i = 0; i < qubits.size(); ++i)
1204 measVec[i] = meas.at(qubits[i]);
1205
1206 ++result[measVec];
1207 }
1208 } else if (qset.size() == 1) {
1209 // if only one qubit is measured, we can use the probability
1210 normal = false;
1211 const auto prob0 = mpsSimulator->GetProbability(qubits[0]);
1212 for (size_t shot = 0; shot < shots; ++shot) {
1213 const size_t meas = uniformZeroOne(rng) < prob0 ? 0ULL : 1ULL;
1214 const std::vector<bool> m(qubits.size(), meas);
1215 ++result[m];
1216 }
1217 }
1218 }
1219
1220 if (normal) {
1221 auto savedState = mpsSimulator->getState();
1222 for (size_t shot = 0; shot < shots; ++shot) {
1223 const auto meas = MeasureMany(qubits);
1224
1225 ++result[meas];
1226 mpsSimulator->setState(savedState);
1227 }
1228 }
1229 } else if (simulationType == SimulationType::kStabilizer) {
1230 cliffordSimulator->SaveState();
1231 for (size_t shot = 0; shot < shots; ++shot) {
1232 const auto meas = MeasureMany(qubits);
1233 ++result[meas];
1234 cliffordSimulator->RestoreState();
1235 }
1236 cliffordSimulator->ClearSavedState();
1237 } else if (simulationType == SimulationType::kTensorNetwork) {
1238 tensorNetwork->SaveState();
1239 for (size_t shot = 0; shot < shots; ++shot) {
1240 const auto meas = MeasureMany(qubits);
1241 ++result[meas];
1242 tensorNetwork->RestoreState();
1243 }
1244 tensorNetwork->ClearSavedState();
1245 } else if (simulationType == SimulationType::kPauliPropagator) {
1246 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
1247 for (size_t shot = 0; shot < shots; ++shot) {
1248 const auto meas = pp->Sample(qubitsInt);
1249 ++result[meas];
1250 }
1251 } else if (simulationType == SimulationType::kPathIntegral) {
1252 if (nrQubits < 64) {
1253 if (shots > 1) {
1254 const auto &amplitudes = pathIntegralSimulator->Amplitudes();
1255 const Utils::Alias alias(amplitudes);
1256 for (size_t shot = 0; shot < shots; ++shot) {
1257 const double prob = 1. - uniformZeroOne(rng);
1258 const size_t measRaw = alias.Sample(prob);
1259 std::vector<bool> meas(qubits.size(), false);
1260 for (size_t i = 0; i < qubits.size(); ++i)
1261 if (((measRaw >> qubits[i]) & 1) == 1) meas[i] = true;
1262 ++result[meas];
1263 }
1264 } else {
1265 for (size_t shot = 0; shot < shots; ++shot) {
1266 const auto measRaw = MeasureNoCollapseMany();
1267 std::vector<bool> meas(qubits.size(), false);
1268
1269 for (size_t i = 0; i < qubits.size(); ++i)
1270 if (measRaw[qubits[i]]) meas[i] = true;
1271
1272 ++result[meas];
1273 }
1274 }
1275 } else {
1276 if (shots > 1) {
1277 const auto &amplitudes = pathIntegralSimulator->Amplitudes();
1278 const Utils::AliasBig alias(amplitudes);
1279
1280 for (size_t shot = 0; shot < shots; ++shot) {
1281 const double prob = 1. - uniformZeroOne(rng);
1282 const auto measRaw = alias.Sample(prob);
1283 std::vector<bool> meas(qubits.size(), false);
1284 for (size_t i = 0; i < qubits.size(); ++i)
1285 if (measRaw.get(qubits[i])) meas[i] = true;
1286 ++result[meas];
1287 }
1288 } else {
1289 for (size_t shot = 0; shot < shots; ++shot) {
1290 const auto measRaw = MeasureNoCollapseMany();
1291 std::vector<bool> meas(qubits.size(), false);
1292
1293 for (size_t i = 0; i < qubits.size(); ++i)
1294 if (measRaw[qubits[i]]) meas[i] = true;
1295
1296 ++result[meas];
1297 }
1298 }
1299 }
1300 } else {
1301 if (shots > 1) {
1302 const auto &statev = state->getRegisterStorage();
1303
1304 const Utils::Alias alias(statev);
1305
1306 for (size_t shot = 0; shot < shots; ++shot) {
1307 const double prob = 1. - uniformZeroOne(rng);
1308 const size_t measRaw = alias.Sample(prob);
1309
1310 std::vector<bool> meas(qubits.size(), false);
1311
1312 for (size_t i = 0; i < qubits.size(); ++i)
1313 if (((measRaw >> qubits[i]) & 1) == 1) meas[i] = true;
1314
1315 ++result[meas];
1316 }
1317 } else {
1318 for (size_t shot = 0; shot < shots; ++shot) {
1319 const auto measRaw = MeasureNoCollapseMany();
1320 std::vector<bool> meas(qubits.size(), false);
1321
1322 for (size_t i = 0; i < qubits.size(); ++i)
1323 if (measRaw[qubits[i]]) meas[i] = true;
1324
1325 ++result[meas];
1326 }
1327 }
1328 }
1329
1330 Notify();
1331 NotifyObservers(qubits);
1332
1333 return result;
1334 }
1335
1347 double ExpectationValue(const std::string &pauliStringOrig) override {
1348 if (pauliStringOrig.empty()) return 1.0;
1349
1350 std::string pauliString = pauliStringOrig;
1351 if (pauliString.size() > GetNumberOfQubits()) {
1352 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i) {
1353 const auto pauliOp = toupper(pauliString[i]);
1354 if (pauliOp != 'I' && pauliOp != 'Z') return 0.0;
1355 }
1356
1357 pauliString.resize(GetNumberOfQubits());
1358 }
1359
1360 if (simulationType == SimulationType::kStabilizer)
1361 return cliffordSimulator->ExpectationValue(pauliString);
1362 else if (simulationType == SimulationType::kTensorNetwork)
1363 return tensorNetwork->ExpectationValue(pauliString);
1364 else if (simulationType == SimulationType::kPauliPropagator)
1365 return pp->ExpectationValue(pauliString);
1366 else if (simulationType == SimulationType::kPathIntegral)
1367 return pathIntegralSimulator->ExpectationValue(pauliString);
1368
1369 // statevector or mps
1370 static const QC::Gates::PauliXGate<> xgate;
1371 static const QC::Gates::PauliYGate<> ygate;
1372 static const QC::Gates::PauliZGate<> zgate;
1373
1374 std::vector<QC::Gates::AppliedGate<Eigen::MatrixXcd>> pauliStringVec;
1375 pauliStringVec.reserve(pauliString.size());
1376
1377 for (size_t q = 0; q < pauliString.size(); ++q) {
1378 switch (toupper(pauliString[q])) {
1379 case 'X': {
1380 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1381 xgate.getRawOperatorMatrix(), static_cast<Types::qubit_t>(q));
1382 pauliStringVec.emplace_back(std::move(ag));
1383 } break;
1384 case 'Y': {
1385 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1386 ygate.getRawOperatorMatrix(), static_cast<Types::qubit_t>(q));
1387 pauliStringVec.emplace_back(std::move(ag));
1388 } break;
1389 case 'Z': {
1390 QC::Gates::AppliedGate<Eigen::MatrixXcd> ag(
1391 zgate.getRawOperatorMatrix(), static_cast<Types::qubit_t>(q));
1392 pauliStringVec.emplace_back(std::move(ag));
1393 } break;
1394 case 'I':
1395 [[fallthrough]];
1396 default:
1397 break;
1398 }
1399 }
1400
1401 if (pauliStringVec.empty()) return 1.0;
1402
1403 if (simulationType == SimulationType::kMatrixProductState)
1404 return mpsSimulator->ExpectationValue(pauliStringVec).real();
1405
1406 return state->ExpectationValue(pauliStringVec).real();
1407 }
1408
1416 SimulatorType GetType() const override { return SimulatorType::kQCSim; }
1417
1426 SimulationType GetSimulationType() const override { return simulationType; }
1427
1436 void Flush() override {}
1437
1447 void SaveStateToInternalDestructive() override {}
1448
1455 void RestoreInternalDestructiveSavedState() override {}
1456
1466 void SaveState() override {
1467 if (simulationType == SimulationType::kMatrixProductState)
1468 mpsSimulator->SaveState();
1469 else if (simulationType == SimulationType::kStabilizer)
1470 cliffordSimulator->SaveState();
1471 else if (simulationType == SimulationType::kTensorNetwork)
1472 tensorNetwork->SaveState();
1473 else if (simulationType == SimulationType::kPauliPropagator)
1474 pp->SaveState();
1475 else if (simulationType == SimulationType::kPathIntegral)
1476 pathIntegralSimulator->SaveState();
1477 else
1478 state->SaveState();
1479 }
1480
1489 void RestoreState() override {
1490 if (simulationType == SimulationType::kMatrixProductState)
1491 mpsSimulator->RestoreState();
1492 else if (simulationType == SimulationType::kStabilizer)
1493 cliffordSimulator->RestoreState();
1494 else if (simulationType == SimulationType::kTensorNetwork)
1495 tensorNetwork->RestoreState();
1496 else if (simulationType == SimulationType::kPauliPropagator)
1497 pp->RestoreState();
1498 else if (simulationType == SimulationType::kPathIntegral)
1499 pathIntegralSimulator->RestoreState();
1500 else
1501 state->RestoreState();
1502 }
1503
1511 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
1512 return Amplitude(outcome);
1513 }
1514
1523 void SetMultithreading(bool multithreading = true) override {
1524 enableMultithreading = multithreading;
1525 if (state) state->SetMultithreading(multithreading);
1526 if (cliffordSimulator) cliffordSimulator->SetMultithreading(multithreading);
1527 if (tensorNetwork) tensorNetwork->SetMultithreading(multithreading);
1528 if (pp) {
1529 if (multithreading)
1530 pp->EnableParallel();
1531 else
1532 pp->DisableParallel();
1533 }
1534 if (pathIntegralSimulator) {
1535 enableMultithreading = false; // not supported for now
1536 }
1537 }
1538
1546 bool GetMultithreading() const override { return enableMultithreading; }
1547
1558 bool IsQcsim() const override { return true; }
1559
1578 if (GetNumberOfQubits() > sizeof(Types::qubit_t) * 8)
1579 std::cerr
1580 << "Warning: The number of qubits to measure is larger than the "
1581 "number of bits in the Types::qubit_t type, the outcome will be "
1582 "undefined"
1583 << std::endl;
1584
1585 if (simulationType == SimulationType::kStatevector)
1586 return state->MeasureNoCollapse();
1587 else if (simulationType == SimulationType::kMatrixProductState) {
1588 const auto measured = mpsSimulator->MeasureNoCollapse();
1589 Types::qubit_t result = 0;
1590 Types::qubit_t mask = 1;
1591 for (Types::qubit_t q = 0; q < measured.size(); ++q) {
1592 if (measured.at(q)) result |= mask;
1593 mask <<= 1;
1594 }
1595 return result;
1596 } else if (simulationType == SimulationType::kPauliPropagator) {
1597 std::vector<int> qubitsInt(GetNumberOfQubits());
1598 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
1599 const auto res = pp->Sample(qubitsInt);
1600 Types::qubit_t result = 0;
1601 for (size_t i = 0; i < res.size(); ++i) {
1602 if (res[i]) result |= (1ULL << i);
1603 }
1604 return result;
1605 } else if (simulationType == SimulationType::kPathIntegral) {
1606 if (nrQubits < 64) {
1607 const auto measured = pathIntegralSimulator->MeasureNoCollapse();
1608 Types::qubit_t result = 0;
1609 Types::qubit_t mask = 1;
1610 for (Types::qubit_t q = 0; q < measured.size(); ++q) {
1611 if (measured.get(q)) result |= mask;
1612 mask <<= 1;
1613 }
1614 return result;
1615 } else {
1616 throw std::runtime_error(
1617 "QCSimState::MeasureNoCollapse: The path integral simulator does not "
1618 "support measuring more than 63 qubits into 64 bits integers.");
1619 }
1620 }
1621
1622 throw std::runtime_error(
1623 "QCSimState::MeasureNoCollapse: Invalid simulation type for "
1624 "measuring "
1625 "all the qubits without collapsing the state.");
1626
1627 return 0;
1628 }
1629
1645 std::vector<bool> MeasureNoCollapseMany() override {
1646 if (simulationType == SimulationType::kStatevector) {
1647 auto state = MeasureNoCollapse();
1648 std::vector<bool> res(nrQubits);
1649 for (size_t i = 0; i < nrQubits; ++i) res[i] = ((state >> i) & 1) == 1;
1650 return res;
1651 } else if (simulationType == SimulationType::kMatrixProductState) {
1652 const auto measured = mpsSimulator->MeasureNoCollapse();
1653 std::vector<bool> res(nrQubits);
1654 for (size_t i = 0; i < nrQubits; ++i) res[i] = measured.at(i);
1655 return res;
1656 } else if (simulationType == SimulationType::kPauliPropagator) {
1657 std::vector<int> qubitsInt(GetNumberOfQubits());
1658 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
1659 return pp->Sample(qubitsInt);
1660 } else if (simulationType == SimulationType::kPathIntegral) {
1661 const auto measured = pathIntegralSimulator->MeasureNoCollapse();
1662 std::vector<bool> res(nrQubits);
1663 for (size_t i = 0; i < nrQubits; ++i) res[i] = measured.get(i);
1664 return res;
1665 }
1666
1667 throw std::runtime_error(
1668 "QCSimState::MeasureNoCollapseMany: Invalid simulation type for "
1669 "measuring all the qubits without collapsing the state.");
1670
1671 return {};
1672 }
1673
1674 protected:
1675 SimulationType simulationType =
1676 SimulationType::kStatevector;
1677
1678 std::unique_ptr<QC::QubitRegister<>> state;
1679 std::unique_ptr<QC::TensorNetworks::MPSSimulator>
1680 mpsSimulator;
1681 std::unique_ptr<QC::Clifford::StabilizerSimulator>
1682 cliffordSimulator;
1683 std::unique_ptr<TensorNetworks::TensorNetwork>
1684 tensorNetwork;
1685 std::unique_ptr<QcsimPauliPropagator> pp;
1686 std::unique_ptr<PathIntegralSimulator>
1687 pathIntegralSimulator;
1688
1689 size_t nrQubits = 0;
1690 bool limitSize = false;
1691 bool limitEntanglement = false;
1692 Eigen::Index chi = 10; // if limitSize is true
1693 double singularValueThreshold = 0.; // if limitEntanglement is true
1694 bool enableMultithreading = true;
1695 bool useMPSMeasureNoCollapse =
1696 true;
1697
1698 int lookaheadDepth = 0;
1699 int lookaheadDepthWithHeuristic = 0;
1700 bool useOptimalMeetingPosition = true;
1701 std::vector<std::shared_ptr<Circuits::IOperation<>>> upcomingGates;
1702 long long int upcomingGateIndex = 0;
1703 double growthFactorSwap = 1.;
1704 double growthFactorGate = 0.65;
1705
1706 std::unique_ptr<Simulators::MPSDummySimulator> dummySim;
1707
1708 // Observer that counts applied gates to track position in upcomingGates
1709 class GateCounterObserver : public ISimulatorObserver {
1710 public:
1711 GateCounterObserver(long long int &indexRef) : index(indexRef) {}
1712 void Update(const Types::qubits_vector &) override { ++index; }
1713
1714 private:
1715 long long int &index;
1716 };
1717 std::shared_ptr<GateCounterObserver> gateCounterObserver;
1718
1719 std::mt19937_64 rng;
1720 std::uniform_real_distribution<double> uniformZeroOne;
1721};
1722
1723} // namespace Private
1724} // namespace Simulators
1725
1726#endif
1727
1728#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