Maestro 0.2.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuState.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _GPUSTATE_H_
16#define _GPUSTATE_H_
17
18#ifdef __linux__
19
20#ifdef INCLUDED_BY_FACTORY
21
22#include "MPSDummySimulator.h"
23
24#include <cstdint>
25#include <iomanip>
26#include <limits>
27#include <sstream>
28
29namespace Simulators {
30// TODO: Maybe use the pimpl idiom
31// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
32// for good but during development this should be good enough
33namespace Private {
34
45class GpuState : public ISimulator {
46 public:
54 void Initialize() override {
55 if (nrQubits) {
56 if (simulationType == SimulationType::kStatevector) {
57 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
58 if (state) {
59 const bool res = state->Create(nrQubits);
60 if (!res)
61 throw std::runtime_error(
62 "GpuState::Initialize: Failed to create "
63 "and initialize the statevector state.");
64 } else
65 throw std::runtime_error(
66 "GpuState::Initialize: Failed to create the statevector state.");
67 } else if (simulationType == SimulationType::kMatrixProductState) {
68 mps = SimulatorsFactory::CreateGpuLibMPSSim();
69 if (mps) {
70 if (useDoublePrecision) mps->SetDataType(true);
71 if (limitEntanglement && singularValueThreshold > 0.)
72 mps->SetCutoff(singularValueThreshold);
73 if (limitSize && chi > 0) mps->SetMaxExtent(chi);
74 const bool res = mps->Create(nrQubits);
75 if (!res)
76 throw std::runtime_error(
77 "GpuState::Initialize: Failed to create "
78 "and initialize the MPS state.");
79 } else
80 throw std::runtime_error(
81 "GpuState::Initialize: Failed to create the MPS state.");
82 // default is true
83 if (!useOptimalMeetingPosition)
84 mps->SetUseOptimalMeetingPosition(false);
85 } else if (simulationType == SimulationType::kTensorNetwork) {
86 tn = SimulatorsFactory::CreateGpuLibTensorNetSim();
87 if (tn) {
88 const bool res = tn->Create(nrQubits);
89 if (!res)
90 throw std::runtime_error(
91 "GpuState::Initialize: Failed to create "
92 "and initialize the tensor network state.");
93 } else
94 throw std::runtime_error(
95 "GpuState::Initialize: Failed to create the tensor network "
96 "state.");
97 } else if (simulationType == SimulationType::kPauliPropagator) {
98 pp = SimulatorsFactory::CreateGpuPauliPropagatorSimulatorUnique();
99 if (pp) {
100 const bool res = pp->CreateSimulator(nrQubits);
101 if (!res)
102 throw std::runtime_error(
103 "GpuState::Initialize: Failed to create "
104 "and initialize the Pauli propagator state.");
105
106 pp->SetWillUseSampling(true); // TODO: check setting
107 if (!pp->AllocateMemory(0.9))
108 throw std::runtime_error(
109 "GpuState::Initialize: Failed to allocate memory for the "
110 "Pauli propagator state.");
111 } else
112 throw std::runtime_error(
113 "GpuState::Initialize: Failed to create the Pauli propagator "
114 "state.");
115 } else
116 throw std::runtime_error(
117 "GpuState::Initialize: Invalid simulation "
118 "type for initializing the state.");
119 }
120 }
121
133 void InitializeState(size_t num_qubits,
134 std::vector<std::complex<double>> &amplitudes) override {
135 if (num_qubits == 0) return;
136 Clear();
137 nrQubits = num_qubits;
138 Initialize();
139
140 if (simulationType != SimulationType::kStatevector)
141 throw std::runtime_error(
142 "GpuState::InitializeState: Invalid simulation "
143 "type for initializing the state.");
144
145 if (nrQubits) {
146 if (simulationType == SimulationType::kStatevector) {
147 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
148 if (state)
149 state->CreateWithState(
150 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
151 }
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
174 if (simulationType != SimulationType::kStatevector)
175 throw std::runtime_error(
176 "GpuState::InitializeState: Invalid simulation "
177 "type for initializing the state.");
178
179 if (nrQubits) {
180 if (simulationType == SimulationType::kStatevector) {
181 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
182 if (state)
183 state->CreateWithState(
184 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
185 }
186 }
187 }
188#endif
189
201 void InitializeState(size_t num_qubits,
202 Eigen::VectorXcd &amplitudes) override {
203 if (num_qubits == 0) return;
204 Clear();
205 nrQubits = num_qubits;
206 Initialize();
207
208 if (simulationType != SimulationType::kStatevector)
209 throw std::runtime_error(
210 "GpuState::InitializeState: Invalid simulation "
211 "type for initializing the state.");
212
213 if (nrQubits) {
214 if (simulationType == SimulationType::kStatevector) {
215 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
216 if (state)
217 state->CreateWithState(
218 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
219 }
220 }
221 }
222
229 void Reset() override {
230 if (state)
231 state->Reset();
232 else if (mps)
233 mps->Reset();
234 else if (tn)
235 tn->Reset();
236 else if (pp)
237 pp->ClearOperators();
238
239 upcomingGateIndex = 0;
240 }
241
249 bool SupportsMPSSwapOptimization() const override { return true; }
250
259 void SetInitialQubitsMap(
260 const std::vector<long long int> &initialMap) override {
261 if (mps) {
262 mps->SetInitialQubitsMap(initialMap);
263 if (!dummySim || dummySim->getNrQubits() != initialMap.size()) {
264 dummySim =
265 std::make_unique<Simulators::MPSDummySimulator>(initialMap.size());
266 dummySim->SetMaxBondDimension(
267 limitSize ? static_cast<long long int>(chi) : 0);
268 }
269 dummySim->setGrowthFactorGate(growthFactorGate);
270 dummySim->setGrowthFactorSwap(growthFactorSwap);
271 dummySim->SetInitialQubitsMap(initialMap);
272 }
273 }
274
275 void SetUseOptimalMeetingPosition(bool enable) override {
276 useOptimalMeetingPosition = enable;
277 if (mps) mps->SetUseOptimalMeetingPosition(enable);
278 }
279
280 void SetLookaheadDepth(int depth) override {
281 lookaheadDepth = depth;
282 if (mps && depth > 0 && !useOptimalMeetingPosition)
283 mps->SetUseOptimalMeetingPosition(true);
284 }
285
286 void SetLookaheadDepthWithHeuristic(int depth) override {
287 lookaheadDepthWithHeuristic = depth;
288 if (lookaheadDepth < depth) SetLookaheadDepth(depth);
289 }
290
291 void SetUpcomingGates(
292 const std::vector<std::shared_ptr<Circuits::IOperation<double>>> &gates)
293 override {
294 upcomingGates = gates;
295 upcomingGateIndex = 0;
296
297 if (!mps || lookaheadDepth <= 0) return;
298
299 // Register an observer that advances the gate index
300 ClearObservers(); // for now we only have this observer, so this should be
301 // fine
302 gateCounterObserver =
303 std::make_shared<GateCounterObserver>(upcomingGateIndex);
304 RegisterObserver(gateCounterObserver);
305
306 // Set up a meeting position callback that uses MPSDummySimulator
307 // for lookahead evaluation with actual bond dimensions
308 // the callback is called only for two qubits gates and only if executing
309 // them would require a swap
310 mps->SetCallbackContext((void*)this);
311 mps->SetMeetingPositionCallback(&GpuState::FindBestMeetingPosition);
312 }
313
322 long long int GetGatesCounter() const override { return upcomingGateIndex; }
323
333 void SetGatesCounter(long long int counter) override {
334 upcomingGateIndex = counter;
335 }
336
345 void IncrementGatesCounter() override { ++upcomingGateIndex; }
346
347 double getGrowthFactorSwap() const override { return growthFactorSwap; }
348 double getGrowthFactorGate() const override { return growthFactorGate; }
349
350 void setGrowthFactorSwap(double factor) override {
351 growthFactorSwap = factor;
352 if (dummySim) dummySim->setGrowthFactorSwap(factor);
353 }
354
355 void setGrowthFactorGate(double factor) override {
356 growthFactorGate = factor;
357 if (dummySim) dummySim->setGrowthFactorGate(factor);
358 }
359
368 void Configure(const char *key, const char *value) override {
369 if (std::string("method") == key) {
370 if (std::string("statevector") == value)
371 simulationType = SimulationType::kStatevector;
372 else if (std::string("matrix_product_state") == value)
373 simulationType = SimulationType::kMatrixProductState;
374 else if (std::string("tensor_network") == value)
375 simulationType = SimulationType::kTensorNetwork;
376 else if (std::string("pauli_propagator") == value)
377 simulationType = SimulationType::kPauliPropagator;
378 } else if (std::string("matrix_product_state_truncation_threshold") ==
379 key) {
380 singularValueThreshold = std::stod(value);
381 if (singularValueThreshold > 0.) {
382 limitEntanglement = true;
383 if (mps) mps->SetCutoff(singularValueThreshold);
384 if (tn) tn->SetCutoff(singularValueThreshold);
385 } else
386 limitEntanglement = false;
387 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
388 chi = std::stoi(value);
389 if (chi > 0) {
390 limitSize = true;
391 if (mps) mps->SetMaxExtent(chi);
392 if (tn) tn->SetMaxExtent(chi);
393 } else
394 limitSize = false;
395 } else if (std::string("use_double_precision") == key) {
396 useDoublePrecision =
397 (std::string("1") == value || std::string("true") == value);
398 }
399 // TODO: add pauli propagator configuration options
400 }
401
409 std::string GetConfiguration(const char *key) const override {
410 if (std::string("method") == key) {
411 switch (simulationType) {
412 case SimulationType::kStatevector:
413 return "statevector";
414 case SimulationType::kMatrixProductState:
415 return "matrix_product_state";
416 case SimulationType::kTensorNetwork:
417 return "tensor_network";
418 case SimulationType::kPauliPropagator:
419 return "pauli_propagator";
420 default:
421 return "other";
422 }
423 } else if (std::string("matrix_product_state_truncation_threshold") ==
424 key) {
425 if (limitEntanglement && singularValueThreshold > 0.) {
426 std::ostringstream oss;
427 oss << std::setprecision(std::numeric_limits<double>::max_digits10)
428 << singularValueThreshold;
429 return oss.str();
430 }
431 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
432 if (limitSize && limitSize > 0) return std::to_string(chi);
433 }
434 // TODO: add pauli propagator configuration options
435
436 return "";
437 }
438
446 size_t AllocateQubits(size_t num_qubits) override {
447 if ((simulationType == SimulationType::kStatevector && state) ||
448 (simulationType == SimulationType::kMatrixProductState && mps) ||
449 (simulationType == SimulationType::kPauliPropagator && pp))
450 return 0;
451
452 const size_t oldNrQubits = nrQubits;
453 nrQubits += num_qubits;
454
455 return oldNrQubits;
456 }
457
464 size_t GetNumberOfQubits() const override { return nrQubits; }
465
473 void Clear() override {
474 state = nullptr;
475 mps = nullptr;
476 tn = nullptr;
477 pp = nullptr;
478 nrQubits = 0;
479 dummySim = nullptr;
480 upcomingGateIndex = 0;
481 upcomingGates.clear();
482 }
483
494 size_t Measure(const Types::qubits_vector &qubits) override {
495 // TODO: this is inefficient, maybe implement it better in gpu sim
496 // for now it has the possibility of measuring a qubits interval, but not a
497 // list of qubits
498 if (qubits.size() > sizeof(size_t) * 8)
499 std::cerr
500 << "Warning: The number of qubits to measure is larger than the "
501 "number of bits in the size_t type, the outcome will be undefined"
502 << std::endl;
503
504 size_t res = 0;
505 size_t mask = 1ULL;
506
507 DontNotify();
508 if (simulationType == SimulationType::kStatevector) {
509 // TODO: measure all qubits in one shot?
510 for (size_t qubit : qubits) {
511 if (state->MeasureQubitCollapse(static_cast<int>(qubit))) res |= mask;
512 mask <<= 1;
513 }
514 } else if (simulationType == SimulationType::kMatrixProductState) {
515 // TODO: measure all qubits in one shot?
516 for (size_t qubit : qubits) {
517 if (mps->Measure(static_cast<unsigned int>(qubit))) res |= mask;
518 mask <<= 1;
519 }
520 } else if (simulationType == SimulationType::kTensorNetwork) {
521 // TODO: measure all qubits in one shot?
522 for (size_t qubit : qubits) {
523 if (tn->Measure(static_cast<unsigned int>(qubit))) res |= mask;
524 mask <<= 1;
525 }
526 } else if (simulationType == SimulationType::kPauliPropagator) {
527 // TODO: measure all qubits in one shot?
528 for (size_t qubit : qubits) {
529 if (pp->MeasureQubit(static_cast<int>(qubit))) res |= mask;
530 mask <<= 1;
531 }
532 }
533
534 Notify();
535 NotifyObservers(qubits);
536
537 return res;
538 }
539
546 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
547 std::vector<bool> res(qubits.size(), false);
548
549 DontNotify();
550 if (simulationType == SimulationType::kStatevector) {
551 for (size_t i = 0; i < qubits.size(); ++i)
552 res[i] = state->MeasureQubitCollapse(static_cast<int>(qubits[i]));
553 } else if (simulationType == SimulationType::kMatrixProductState) {
554 for (size_t i = 0; i < qubits.size(); ++i)
555 res[i] = mps->Measure(static_cast<unsigned int>(qubits[i]));
556 } else if (simulationType == SimulationType::kTensorNetwork) {
557 for (size_t i = 0; i < qubits.size(); ++i)
558 res[i] = tn->Measure(static_cast<unsigned int>(qubits[i]));
559 } else if (simulationType == SimulationType::kPauliPropagator) {
560 for (size_t i = 0; i < qubits.size(); ++i)
561 res[i] = pp->MeasureQubit(static_cast<int>(qubits[i]));
562 }
563 Notify();
564 NotifyObservers(qubits);
565
566 return res;
567 }
568
575 void ApplyReset(const Types::qubits_vector &qubits) override {
576 DontNotify();
577 if (simulationType == SimulationType::kStatevector) {
578 for (size_t qubit : qubits)
579 if (state->MeasureQubitCollapse(static_cast<int>(qubit)))
580 state->ApplyX(static_cast<int>(qubit));
581 } else if (simulationType == SimulationType::kMatrixProductState) {
582 for (size_t qubit : qubits)
583 if (mps->Measure(static_cast<unsigned int>(qubit)))
584 mps->ApplyX(static_cast<unsigned int>(qubit));
585 } else if (simulationType == SimulationType::kTensorNetwork) {
586 for (size_t qubit : qubits)
587 if (tn->Measure(static_cast<unsigned int>(qubit)))
588 tn->ApplyX(static_cast<unsigned int>(qubit));
589 } else if (simulationType == SimulationType::kPauliPropagator) {
590 for (size_t qubit : qubits)
591 if (pp->MeasureQubit(static_cast<int>(qubit)))
592 pp->ApplyX(static_cast<int>(qubit));
593 }
594
595 Notify();
596 NotifyObservers(qubits);
597 }
598
610 double Probability(Types::qubit_t outcome) override {
611 if (simulationType == SimulationType::kStatevector)
612 return state->BasisStateProbability(outcome);
613 else if (simulationType == SimulationType::kMatrixProductState ||
614 simulationType == SimulationType::kTensorNetwork) {
615 const auto ampl = Amplitude(outcome);
616 return std::norm(ampl);
617 } else if (simulationType == SimulationType::kPauliPropagator) {
618 return pp->Probability(outcome);
619 }
620
621 return 0.0;
622 }
623
634 std::complex<double> Amplitude(Types::qubit_t outcome) override {
635 double real = 0.0;
636 double imag = 0.0;
637
638 if (simulationType == SimulationType::kStatevector)
639 state->Amplitude(outcome, &real, &imag);
640 else if (simulationType == SimulationType::kMatrixProductState ||
641 simulationType == SimulationType::kTensorNetwork) {
642 std::vector<long int> fixedValues(nrQubits);
643 for (size_t i = 0; i < nrQubits; ++i)
644 fixedValues[i] = (outcome & (1ULL << i)) ? 1 : 0;
645 if (simulationType == SimulationType::kMatrixProductState)
646 mps->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
647 else if (simulationType == SimulationType::kTensorNetwork)
648 tn->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
649 } else if (simulationType == SimulationType::kPauliPropagator) {
650 // Pauli propagator does not support amplitude calculation
651 throw std::runtime_error(
652 "GpuState::Amplitude: Invalid simulation type for amplitude "
653 "calculation.");
654 }
655
656 return std::complex<double>(real, imag);
657 }
658
672 std::complex<double> ProjectOnZero() override {
673 if (simulationType == SimulationType::kMatrixProductState)
674 return mps->ProjectOnZero();
675
676 return Amplitude(0);
677 }
678
689 std::vector<double> AllProbabilities() override {
690 if (nrQubits == 0) return {};
691 const size_t numStates = 1ULL << nrQubits;
692 std::vector<double> result(numStates);
693
694 if (simulationType == SimulationType::kStatevector)
695 state->AllProbabilities(result.data());
696 else if (simulationType == SimulationType::kMatrixProductState ||
697 simulationType == SimulationType::kTensorNetwork) {
698 // this is very slow, it should be used only for tests!
699 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
700 const auto val = Amplitude(i);
701 result[i] = std::norm(std::complex<double>(val.real(), val.imag()));
702 }
703 } else if (simulationType == SimulationType::kPauliPropagator) {
704 // this is very slow, it should be used only for tests!
705 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
706 result[i] = pp->Probability(i);
707 }
708 }
709
710 return result;
711 }
712
724 std::vector<double> Probabilities(
725 const Types::qubits_vector &qubits) override {
726 std::vector<double> result(qubits.size());
727
728 if (simulationType == SimulationType::kStatevector) {
729 for (size_t i = 0; i < qubits.size(); ++i)
730 result[i] = state->BasisStateProbability(qubits[i]);
731 } else if (simulationType == SimulationType::kMatrixProductState ||
732 simulationType == SimulationType::kTensorNetwork) {
733 for (size_t i = 0; i < qubits.size(); ++i) {
734 const auto ampl = Amplitude(qubits[i]);
735 result[i] = std::norm(ampl);
736 }
737 } else if (simulationType == SimulationType::kPauliPropagator) {
738 for (size_t i = 0; i < qubits.size(); ++i)
739 result[i] = pp->Probability(qubits[i]);
740 }
741
742 return result;
743 }
744
761 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
762 const Types::qubits_vector &qubits, size_t shots = 1000) override {
763 if (qubits.empty() || shots == 0) return {};
764
765 if (qubits.size() > sizeof(Types::qubit_t) * 8)
766 std::cerr
767 << "Warning: The number of qubits to measure is larger than the "
768 "number of bits in the Types::qubit_t type, the outcome will be "
769 "undefined"
770 << std::endl;
771
772 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
773
774 DontNotify();
775
776 if (simulationType == SimulationType::kStatevector) {
777 std::vector<long int> samples(shots);
778 state->SampleAll(shots, samples.data());
779
780 for (auto outcome : samples) {
781 // qubits might not be in order, translate the outcome to the correct
782 // order
783 Types::qubit_t translatedOutcome = 0;
784 Types::qubit_t mask = 1ULL;
785 for (size_t i = 0; i < qubits.size(); ++i) {
786 if (outcome & (1ULL << qubits[i])) translatedOutcome |= mask;
787 mask <<= 1;
788 }
789 ++result[translatedOutcome];
790 }
791 } else if (simulationType == SimulationType::kMatrixProductState) {
792 std::unordered_map<std::vector<bool>, int64_t> *map =
793 mps->GetMapForSample();
794
795 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
796
797 mps->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
798
799 // put the results in the result map
800 for (const auto &[meas, cnt] : *map) {
801 Types::qubit_t outcome = 0;
802 Types::qubit_t mask = 1ULL;
803 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
804 if (meas[q]) outcome |= mask;
805 mask <<= 1;
806 }
807
808 result[outcome] += cnt;
809 }
810
811 mps->FreeMapForSample(map);
812 } else if (simulationType == SimulationType::kTensorNetwork) {
813 std::unordered_map<std::vector<bool>, int64_t> *map =
814 tn->GetMapForSample();
815 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
816 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
817 // put the results in the result map
818 for (const auto &[meas, cnt] : *map) {
819 Types::qubit_t outcome = 0;
820 Types::qubit_t mask = 1ULL;
821 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
822 if (meas[q]) outcome |= mask;
823 mask <<= 1;
824 }
825 result[outcome] += cnt;
826 }
827 tn->FreeMapForSample(map);
828 } else if (simulationType == SimulationType::kPauliPropagator) {
829 std::vector<int> qb(qubits.begin(), qubits.end());
830 for (size_t shot = 0; shot < shots; ++shot) {
831 size_t meas = 0;
832 auto res = pp->SampleQubits(qb);
833 for (size_t i = 0; i < qubits.size(); ++i) {
834 if (res[i]) meas |= (1ULL << i);
835 }
836 ++result[meas];
837 }
838 }
839
840 Notify();
841 NotifyObservers(qubits);
842
843 return result;
844 }
845
859 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
860 const Types::qubits_vector &qubits, size_t shots = 1000) override {
861 if (qubits.empty() || shots == 0) return {};
862
863 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
864
865 DontNotify();
866
867 if (simulationType == SimulationType::kStatevector) {
868 std::vector<long int> samples(shots);
869 state->SampleAll(shots, samples.data());
870
871 std::vector<bool> outcomeVec(qubits.size());
872 for (auto outcome : samples) {
873 for (size_t i = 0; i < qubits.size(); ++i)
874 outcomeVec[i] = ((outcome >> qubits[i]) & 1) == 1;
875 ++result[outcomeVec];
876 }
877 } else if (simulationType == SimulationType::kMatrixProductState) {
878 std::unordered_map<std::vector<bool>, int64_t> *map =
879 mps->GetMapForSample();
880
881 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
882 mps->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
883
884 // put the results in the result map
885 for (const auto &[meas, cnt] : *map) result[meas] += cnt;
886
887 mps->FreeMapForSample(map);
888 } else if (simulationType == SimulationType::kTensorNetwork) {
889 std::unordered_map<std::vector<bool>, int64_t> *map =
890 tn->GetMapForSample();
891 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
892 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
893 // put the results in the result map
894 for (const auto &[meas, cnt] : *map) result[meas] += cnt;
895 tn->FreeMapForSample(map);
896 } else if (simulationType == SimulationType::kPauliPropagator) {
897 std::vector<int> qb(qubits.begin(), qubits.end());
898 for (size_t shot = 0; shot < shots; ++shot) {
899 const auto res = pp->SampleQubits(qb);
900 ++result[res];
901 }
902 }
903
904 Notify();
905 NotifyObservers(qubits);
906
907 return result;
908 }
909
921 double ExpectationValue(const std::string &pauliString) override {
922 double result = 0.0;
923
924 if (simulationType == SimulationType::kStatevector)
925 result = state->ExpectationValue(pauliString);
926 else if (simulationType == SimulationType::kMatrixProductState)
927 result = mps->ExpectationValue(pauliString);
928 else if (simulationType == SimulationType::kTensorNetwork)
929 result = tn->ExpectationValue(pauliString);
930 else if (simulationType == SimulationType::kPauliPropagator)
931 result = pp->ExpectationValue(pauliString);
932 else
933 throw std::runtime_error(
934 "GpuState::ExpectationValue: Invalid simulation type for expectation "
935 "value calculation.");
936
937 return result;
938 }
939
947 SimulatorType GetType() const override { return SimulatorType::kGpuSim; }
948
957 SimulationType GetSimulationType() const override { return simulationType; }
958
967 void Flush() override {}
968
979 void SaveStateToInternalDestructive() override {
980 if (simulationType == SimulationType::kStatevector)
981 state->SaveStateDestructive();
982 else
983 throw std::runtime_error(
984 "GpuState::SaveStateToInternalDestructive: Invalid simulation type "
985 "for saving the state destructively.");
986 }
987
995 if (simulationType == SimulationType::kStatevector)
996 state->RestoreStateFreeSaved();
997 else
998 throw std::runtime_error(
999 "GpuState::RestoreInternalDestructiveSavedState: Invalid simulation "
1000 "type for restoring the state destructively.");
1001 }
1002
1011 void SaveState() override {
1012 if (simulationType == SimulationType::kStatevector)
1013 state->SaveState();
1014 else if (simulationType == SimulationType::kMatrixProductState)
1015 mps->SaveState();
1016 else if (simulationType == SimulationType::kTensorNetwork)
1017 tn->SaveState();
1018 else if (simulationType == SimulationType::kPauliPropagator)
1019 pp->SaveState();
1020 }
1021
1029 void RestoreState() override {
1030 if (simulationType == SimulationType::kStatevector)
1031 state->RestoreStateNoFreeSaved();
1032 else if (simulationType == SimulationType::kMatrixProductState)
1033 mps->RestoreState();
1034 else if (simulationType == SimulationType::kTensorNetwork)
1035 tn->RestoreState();
1036 else if (simulationType == SimulationType::kPauliPropagator)
1037 pp->RestoreState();
1038 }
1039
1047 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
1048 return Amplitude(outcome);
1049 }
1050
1059 void SetMultithreading(bool multithreading = true) override {
1060 // don't do anything here, the multithreading is always enabled
1061 }
1062
1070 bool GetMultithreading() const override { return true; }
1071
1082 bool IsQcsim() const override { return false; }
1083
1101 if (simulationType == SimulationType::kStatevector)
1102 return state->MeasureAllQubitsNoCollapse();
1103 else if (simulationType == SimulationType::kMatrixProductState ||
1104 simulationType == SimulationType::kTensorNetwork ||
1105 simulationType == SimulationType::kPauliPropagator) {
1106 if (nrQubits > sizeof(Types::qubit_t) * 8)
1107 std::cerr
1108 << "Warning: The number of qubits to measure is larger than the "
1109 "number of bits in the Types::qubit_t type, the outcome will be "
1110 "undefined"
1111 << std::endl;
1112
1113 Types::qubits_vector fixedValues(nrQubits);
1114 std::iota(fixedValues.begin(), fixedValues.end(), 0);
1115 const auto res = SampleCounts(fixedValues, 1);
1116 if (res.empty()) return 0;
1117 return res.begin()
1118 ->first; // return the first outcome, as it is the only one
1119 }
1120
1121 throw std::runtime_error(
1122 "QCSimState::MeasureNoCollapse: Invalid simulation type for measuring "
1123 "all the qubits without collapsing the state.");
1124
1125 return 0;
1126 }
1127
1142 std::vector<bool> MeasureNoCollapseMany() override {
1143 if (simulationType == SimulationType::kStatevector) {
1144 const auto meas = state->MeasureAllQubitsNoCollapse();
1145 std::vector<bool> result(nrQubits, false);
1146 for (size_t i = 0; i < nrQubits; ++i) result[i] = ((meas >> i) & 1) == 1;
1147 return result;
1148 } else if (simulationType == SimulationType::kMatrixProductState ||
1149 simulationType == SimulationType::kTensorNetwork ||
1150 simulationType == SimulationType::kPauliPropagator) {
1151 Types::qubits_vector fixedValues(nrQubits);
1152 std::iota(fixedValues.begin(), fixedValues.end(), 0);
1153 const auto res = SampleCountsMany(fixedValues, 1);
1154 if (res.empty()) return std::vector<bool>(nrQubits, false);
1155 return res.begin()
1156 ->first; // return the first outcome, as it is the only one
1157 }
1158
1159 throw std::runtime_error(
1160 "QCSimState::MeasureNoCollapseMany: Invalid simulation type for "
1161 "measuring "
1162 "all the qubits without collapsing the state.");
1163
1164 return std::vector<bool>(nrQubits, false);
1165 }
1166
1167 protected:
1168 static int64_t FindBestMeetingPosition(void* thisPtr, const int64_t* bondDims) {
1169 GpuState* self = static_cast<GpuState*>(thisPtr);
1170
1171 return self->FindBestMeetingPositionFunc(bondDims);
1172 };
1173
1174 int64_t FindBestMeetingPositionFunc(const int64_t* bondDims)
1175 {
1176 const size_t nQ = GetNumberOfQubits();
1177
1178 if (!dummySim || dummySim->getNrQubits() != nQ) {
1179 dummySim = std::make_unique<Simulators::MPSDummySimulator>(nQ);
1180 dummySim->SetMaxBondDimension(
1181 limitSize ? static_cast<long long int>(chi) : 0);
1182 dummySim->setGrowthFactorGate(growthFactorGate);
1183 dummySim->setGrowthFactorSwap(growthFactorSwap);
1184 }
1185
1186 dummySim->setTotalSwappingCost(0);
1187 // Convert actual bond dims to doubles
1188 std::vector<double> bondDimsD(bondDims, bondDims + nrQubits - 1);
1189 dummySim->SetCurrentBondDimensions(bondDimsD);
1190
1191 const auto &op = upcomingGates[upcomingGateIndex];
1192 const auto qbits = op->AffectedQubits();
1193
1194 if (qbits.size() != 2) {
1195 std::cerr << "Error: Meeting position callback called for a gate "
1196 "that does not have exactly 2 qubits."
1197 << std::endl;
1198
1199 return -1; // will fallback
1200 }
1201
1202 double bestCost = std::numeric_limits<double>::infinity();
1203 int64_t res = dummySim->FindBestMeetingPosition(
1204 upcomingGates, upcomingGateIndex, lookaheadDepth,
1205 lookaheadDepthWithHeuristic, 0, bestCost);
1206
1207 dummySim->SwapQubitsToPosition(qbits[0], qbits[1], res);
1208 dummySim->ApplyGate(op);
1209
1210 return res;
1211 }
1212
1213 SimulationType simulationType =
1214 SimulationType::kStatevector;
1215
1216 std::unique_ptr<GpuLibStateVectorSim>
1217 state;
1218 std::unique_ptr<GpuLibMPSSim> mps;
1219 std::unique_ptr<GpuLibTNSim> tn;
1220 std::unique_ptr<GpuPauliPropagator>
1221 pp;
1222
1223 size_t nrQubits = 0;
1224 bool limitSize = false;
1225 bool limitEntanglement = false;
1226 Eigen::Index chi = 10; // if limitSize is true
1227 double singularValueThreshold = 0.; // if limitEntanglement is true
1228 bool useDoublePrecision = false; // if true, GPU MPS uses float64
1229
1230 int lookaheadDepth = 0;
1231 int lookaheadDepthWithHeuristic = 0;
1232 bool useOptimalMeetingPosition = true;
1233 std::vector<std::shared_ptr<Circuits::IOperation<>>> upcomingGates;
1234 long long int upcomingGateIndex = 0;
1235 double growthFactorSwap = 1.;
1236 double growthFactorGate = 0.65;
1237
1238 std::unique_ptr<Simulators::MPSDummySimulator> dummySim;
1239
1240 // Observer that counts applied gates to track position in upcomingGates
1241 class GateCounterObserver : public ISimulatorObserver {
1242 public:
1243 GateCounterObserver(long long int &indexRef) : index(indexRef) {}
1244 void Update(const Types::qubits_vector &) override { ++index; }
1245
1246 private:
1247 long long int &index;
1248 };
1249 std::shared_ptr<GateCounterObserver> gateCounterObserver;
1250};
1251
1252} // namespace Private
1253} // namespace Simulators
1254
1255#endif
1256#endif
1257#endif
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
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