Maestro 0.1.0
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuState.h
Go to the documentation of this file.
1
13#pragma once
14
15#ifndef _GPUSTATE_H_
16#define _GPUSTATE_H_
17
18#ifdef __linux__
19
20#ifdef INCLUDED_BY_FACTORY
21
22namespace Simulators {
23// TODO: Maybe use the pimpl idiom
24// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
25// for good but during development this should be good enough
26namespace Private {
27
38class GpuState : public ISimulator {
39 public:
47 void Initialize() override {
48 if (nrQubits) {
49 if (simulationType == SimulationType::kStatevector) {
50 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
51 if (state) {
52 const bool res = state->Create(nrQubits);
53 if (!res)
54 throw std::runtime_error(
55 "GpuState::Initialize: Failed to create "
56 "and initialize the statevector state.");
57 } else
58 throw std::runtime_error(
59 "GpuState::Initialize: Failed to create the statevector state.");
60 } else if (simulationType == SimulationType::kMatrixProductState) {
61 mps = SimulatorsFactory::CreateGpuLibMPSSim();
62 if (mps) {
63 if (limitEntanglement && singularValueThreshold > 0.)
64 mps->SetCutoff(singularValueThreshold);
65 if (limitSize && chi > 0) mps->SetMaxExtent(chi);
66 const bool res = mps->Create(nrQubits);
67 if (!res)
68 throw std::runtime_error(
69 "GpuState::Initialize: Failed to create "
70 "and initialize the MPS state.");
71 } else
72 throw std::runtime_error(
73 "GpuState::Initialize: Failed to create the MPS state.");
74 } else if (simulationType == SimulationType::kTensorNetwork) {
75 tn = SimulatorsFactory::CreateGpuLibTensorNetSim();
76 if (tn) {
77 const bool res = tn->Create(nrQubits);
78 if (!res)
79 throw std::runtime_error(
80 "GpuState::Initialize: Failed to create "
81 "and initialize the tensor network state.");
82 } else
83 throw std::runtime_error(
84 "GpuState::Initialize: Failed to create the tensor network "
85 "state.");
86 } else if (simulationType == SimulationType::kPauliPropagator) {
87 pp = SimulatorsFactory::CreateGpuPauliPropagatorSimulatorUnique();
88 if (pp) {
89 const bool res = pp->CreateSimulator(nrQubits);
90 if (!res)
91 throw std::runtime_error(
92 "GpuState::Initialize: Failed to create "
93 "and initialize the Pauli propagator state.");
94
95 pp->SetWillUseSampling(true); // TODO: check setting
96 if (!pp->AllocateMemory(0.9))
97 throw std::runtime_error(
98 "GpuState::Initialize: Failed to allocate memory for the "
99 "Pauli propagator state.");
100 } else
101 throw std::runtime_error(
102 "GpuState::Initialize: Failed to create the Pauli propagator "
103 "state.");
104 } else
105 throw std::runtime_error(
106 "GpuState::Initialize: Invalid simulation "
107 "type for initializing the state.");
108 }
109 }
110
122 void InitializeState(size_t num_qubits,
123 std::vector<std::complex<double>> &amplitudes) override {
124 if (num_qubits == 0) return;
125 Clear();
126 nrQubits = num_qubits;
127 Initialize();
128
129 if (simulationType != SimulationType::kStatevector)
130 throw std::runtime_error(
131 "GpuState::InitializeState: Invalid simulation "
132 "type for initializing the state.");
133
134 if (nrQubits) {
135 if (simulationType == SimulationType::kStatevector) {
136 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
137 if (state)
138 state->CreateWithState(
139 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
140 }
141 }
142 }
143
155#ifndef NO_QISKIT_AER
156 void InitializeState(size_t num_qubits,
157 AER::Vector<std::complex<double>> &amplitudes) override {
158 if (num_qubits == 0) return;
159 Clear();
160 nrQubits = num_qubits;
161 Initialize();
162
163 if (simulationType != SimulationType::kStatevector)
164 throw std::runtime_error(
165 "GpuState::InitializeState: Invalid simulation "
166 "type for initializing the state.");
167
168 if (nrQubits) {
169 if (simulationType == SimulationType::kStatevector) {
170 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
171 if (state)
172 state->CreateWithState(
173 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
174 }
175 }
176 }
177#endif
178
190 void InitializeState(size_t num_qubits,
191 Eigen::VectorXcd &amplitudes) override {
192 if (num_qubits == 0) return;
193 Clear();
194 nrQubits = num_qubits;
195 Initialize();
196
197 if (simulationType != SimulationType::kStatevector)
198 throw std::runtime_error(
199 "GpuState::InitializeState: Invalid simulation "
200 "type for initializing the state.");
201
202 if (nrQubits) {
203 if (simulationType == SimulationType::kStatevector) {
204 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
205 if (state)
206 state->CreateWithState(
207 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
208 }
209 }
210 }
211
218 void Reset() override {
219 if (state)
220 state->Reset();
221 else if (mps)
222 mps->Reset();
223 else if (tn)
224 tn->Reset();
225 else if (pp)
226 pp->ClearOperators();
227 }
228
237 void Configure(const char *key, const char *value) override {
238 if (std::string("method") == key) {
239 if (std::string("statevector") == value)
240 simulationType = SimulationType::kStatevector;
241 else if (std::string("matrix_product_state") == value)
242 simulationType = SimulationType::kMatrixProductState;
243 else if (std::string("tensor_network") == value)
244 simulationType = SimulationType::kTensorNetwork;
245 else if (std::string("pauli_propagator") == value)
246 simulationType = SimulationType::kPauliPropagator;
247 } else if (std::string("matrix_product_state_truncation_threshold") ==
248 key) {
249 singularValueThreshold = std::stod(value);
250 if (singularValueThreshold > 0.) {
251 limitEntanglement = true;
252 if (mps) mps->SetCutoff(singularValueThreshold);
253 if (tn) tn->SetCutoff(singularValueThreshold);
254 } else
255 limitEntanglement = false;
256 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
257 chi = std::stoi(value);
258 if (chi > 0) {
259 limitSize = true;
260 if (mps) mps->SetMaxExtent(chi);
261 if (tn) tn->SetMaxExtent(chi);
262 } else
263 limitSize = false;
264 }
265 // TODO: add pauli propagator configuration options
266 }
267
275 std::string GetConfiguration(const char *key) const override {
276 if (std::string("method") == key) {
277 switch (simulationType) {
278 case SimulationType::kStatevector:
279 return "statevector";
280 case SimulationType::kMatrixProductState:
281 return "matrix_product_state";
282 case SimulationType::kTensorNetwork:
283 return "tensor_network";
284 case SimulationType::kPauliPropagator:
285 return "pauli_propagator";
286 default:
287 return "other";
288 }
289 } else if (std::string("matrix_product_state_truncation_threshold") ==
290 key) {
291 if (limitEntanglement && singularValueThreshold > 0.)
292 return std::to_string(singularValueThreshold);
293 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
294 if (limitSize && limitSize > 0) return std::to_string(chi);
295 }
296 // TODO: add pauli propagator configuration options
297
298 return "";
299 }
300
308 size_t AllocateQubits(size_t num_qubits) override {
309 if ((simulationType == SimulationType::kStatevector && state) ||
310 (simulationType == SimulationType::kMatrixProductState && mps) ||
311 (simulationType == SimulationType::kPauliPropagator && pp))
312 return 0;
313
314 const size_t oldNrQubits = nrQubits;
315 nrQubits += num_qubits;
316
317 return oldNrQubits;
318 }
319
326 size_t GetNumberOfQubits() const override { return nrQubits; }
327
335 void Clear() override {
336 state = nullptr;
337 mps = nullptr;
338 tn = nullptr;
339 pp = nullptr;
340 nrQubits = 0;
341 }
342
350 size_t Measure(const Types::qubits_vector &qubits) override {
351 // TODO: this is inefficient, maybe implement it better in gpu sim
352 // for now it has the possibility of measuring a qubits interval, but not a
353 // list of qubits
354
355 size_t res = 0;
356 size_t mask = 1ULL;
357
358 DontNotify();
359 if (simulationType == SimulationType::kStatevector) {
360 // TODO: measure all qubits in one shot?
361 for (size_t qubit : qubits) {
362 if (state->MeasureQubitCollapse(static_cast<int>(qubit))) res |= mask;
363 mask <<= 1;
364 }
365 } else if (simulationType == SimulationType::kMatrixProductState) {
366 // TODO: measure all qubits in one shot?
367 for (size_t qubit : qubits) {
368 if (mps->Measure(static_cast<unsigned int>(qubit))) res |= mask;
369 mask <<= 1;
370 }
371 } else if (simulationType == SimulationType::kTensorNetwork) {
372 // TODO: measure all qubits in one shot?
373 for (size_t qubit : qubits) {
374 if (tn->Measure(static_cast<unsigned int>(qubit))) res |= mask;
375 mask <<= 1;
376 }
377 } else if (simulationType == SimulationType::kPauliPropagator) {
378 // TODO: measure all qubits in one shot?
379 for (size_t qubit : qubits) {
380 if (pp->MeasureQubit(static_cast<int>(qubit))) res |= mask;
381 mask <<= 1;
382 }
383 }
384
385 Notify();
386 NotifyObservers(qubits);
387
388 return res;
389 }
390
397 void ApplyReset(const Types::qubits_vector &qubits) override {
398 DontNotify();
399 if (simulationType == SimulationType::kStatevector) {
400 for (size_t qubit : qubits)
401 if (state->MeasureQubitCollapse(static_cast<int>(qubit)))
402 state->ApplyX(static_cast<int>(qubit));
403 } else if (simulationType == SimulationType::kMatrixProductState) {
404 for (size_t qubit : qubits)
405 if (mps->Measure(static_cast<unsigned int>(qubit)))
406 mps->ApplyX(static_cast<unsigned int>(qubit));
407 } else if (simulationType == SimulationType::kTensorNetwork) {
408 for (size_t qubit : qubits)
409 if (tn->Measure(static_cast<unsigned int>(qubit)))
410 tn->ApplyX(static_cast<unsigned int>(qubit));
411 } else if (simulationType == SimulationType::kPauliPropagator) {
412 for (size_t qubit : qubits)
413 if (pp->MeasureQubit(static_cast<int>(qubit)))
414 pp->ApplyX(static_cast<int>(qubit));
415 }
416
417 Notify();
418 NotifyObservers(qubits);
419 }
420
432 double Probability(Types::qubit_t outcome) override {
433 if (simulationType == SimulationType::kStatevector)
434 return state->BasisStateProbability(outcome);
435 else if (simulationType == SimulationType::kMatrixProductState ||
436 simulationType == SimulationType::kTensorNetwork) {
437 const auto ampl = Amplitude(outcome);
438 return std::norm(ampl);
439 } else if (simulationType == SimulationType::kPauliPropagator) {
440 return pp->Probability(outcome);
441 }
442
443 return 0.0;
444 }
445
456 std::complex<double> Amplitude(Types::qubit_t outcome) override {
457 double real = 0.0;
458 double imag = 0.0;
459
460 if (simulationType == SimulationType::kStatevector)
461 state->Amplitude(outcome, &real, &imag);
462 else if (simulationType == SimulationType::kMatrixProductState ||
463 simulationType == SimulationType::kTensorNetwork) {
464 std::vector<long int> fixedValues(nrQubits);
465 for (size_t i = 0; i < nrQubits; ++i)
466 fixedValues[i] = (outcome & (1ULL << i)) ? 1 : 0;
467 if (simulationType == SimulationType::kMatrixProductState)
468 mps->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
469 else if (simulationType == SimulationType::kTensorNetwork)
470 tn->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
471 } else if (simulationType == SimulationType::kPauliPropagator) {
472 // Pauli propagator does not support amplitude calculation
473 throw std::runtime_error(
474 "GpuState::Amplitude: Invalid simulation type for amplitude calculation.");
475 }
476
477 return std::complex<double>(real, imag);
478 }
479
490 std::vector<double> AllProbabilities() override {
491 if (nrQubits == 0) return {};
492 const size_t numStates = 1ULL << nrQubits;
493 std::vector<double> result(numStates);
494
495 if (simulationType == SimulationType::kStatevector)
496 state->AllProbabilities(result.data());
497 else if (simulationType == SimulationType::kMatrixProductState ||
498 simulationType == SimulationType::kTensorNetwork) {
499 // this is very slow, it should be used only for tests!
500 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
501 const auto val = Amplitude(i);
502 result[i] = std::norm(std::complex<double>(val.real(), val.imag()));
503 }
504 } else if (simulationType == SimulationType::kPauliPropagator) {
505 // this is very slow, it should be used only for tests!
506 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
507 result[i] = pp->Probability(i);
508 }
509 }
510
511 return result;
512 }
513
525 std::vector<double> Probabilities(
526 const Types::qubits_vector &qubits) override {
527 std::vector<double> result(qubits.size());
528
529 if (simulationType == SimulationType::kStatevector) {
530 for (size_t i = 0; i < qubits.size(); ++i)
531 result[i] = state->BasisStateProbability(qubits[i]);
532 } else if (simulationType == SimulationType::kMatrixProductState ||
533 simulationType == SimulationType::kTensorNetwork) {
534 for (size_t i = 0; i < qubits.size(); ++i) {
535 const auto ampl = Amplitude(qubits[i]);
536 result[i] = std::norm(ampl);
537 }
538 } else if (simulationType == SimulationType::kPauliPropagator) {
539 for (size_t i = 0; i < qubits.size(); ++i)
540 result[i] = pp->Probability(qubits[i]);
541 }
542
543 return result;
544 }
545
559 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
560 const Types::qubits_vector &qubits, size_t shots = 1000) override {
561 if (qubits.empty() || shots == 0) return {};
562
563 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
564
565 DontNotify();
566
567 if (simulationType == SimulationType::kStatevector) {
568 std::vector<long int> samples(shots);
569 state->SampleAll(shots, samples.data());
570
571 for (auto outcome : samples) ++result[outcome];
572 } else if (simulationType == SimulationType::kMatrixProductState) {
573 std::unordered_map<std::vector<bool>, int64_t> *map =
574 mps->GetMapForSample();
575
576 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
577
578 mps->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
579
580 // put the results in the result map
581 for (const auto &[meas, cnt] : *map) {
582 Types::qubit_t outcome = 0;
583 Types::qubit_t mask = 1ULL;
584 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
585 if (meas[q]) outcome |= mask;
586 mask <<= 1;
587 }
588
589 result[outcome] += cnt;
590 }
591
592 mps->FreeMapForSample(map);
593 } else if (simulationType == SimulationType::kTensorNetwork) {
594 std::unordered_map<std::vector<bool>, int64_t> *map =
595 tn->GetMapForSample();
596 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
597 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
598 // put the results in the result map
599 for (const auto &[meas, cnt] : *map) {
600 Types::qubit_t outcome = 0;
601 Types::qubit_t mask = 1ULL;
602 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
603 if (meas[q]) outcome |= mask;
604 mask <<= 1;
605 }
606 result[outcome] += cnt;
607 }
608 tn->FreeMapForSample(map);
609 } else if (simulationType == SimulationType::kPauliPropagator) {
610 std::vector<int> qb(qubits.begin(), qubits.end());
611 for (size_t shot = 0; shot < shots; ++shot) {
612 size_t meas = 0;
613 auto res = pp->SampleQubits(qb);
614 for (size_t i = 0; i < qubits.size(); ++i) {
615 if (res[i]) meas |= (1ULL << i);
616 }
617 ++result[meas];
618 }
619 }
620
621
622 Notify();
623 NotifyObservers(qubits);
624
625 return result;
626 }
627
639 double ExpectationValue(const std::string &pauliString) override {
640 double result = 0.0;
641
642 if (simulationType == SimulationType::kStatevector)
643 result = state->ExpectationValue(pauliString);
644 else if (simulationType == SimulationType::kMatrixProductState)
645 result = mps->ExpectationValue(pauliString);
646 else if (simulationType == SimulationType::kTensorNetwork)
647 result = tn->ExpectationValue(pauliString);
648 else if (simulationType == SimulationType::kPauliPropagator)
649 result = pp->ExpectationValue(pauliString);
650 else
651 throw std::runtime_error(
652 "GpuState::ExpectationValue: Invalid simulation type for expectation "
653 "value calculation.");
654
655 return result;
656 }
657
665 SimulatorType GetType() const override { return SimulatorType::kGpuSim; }
666
675 SimulationType GetSimulationType() const override { return simulationType; }
676
685 void Flush() override {}
686
697 void SaveStateToInternalDestructive() override {
698 if (simulationType == SimulationType::kStatevector)
699 state->SaveStateDestructive();
700 else
701 throw std::runtime_error(
702 "GpuState::SaveStateToInternalDestructive: Invalid simulation type "
703 "for saving the state destructively.");
704 }
705
713 if (simulationType == SimulationType::kStatevector)
714 state->RestoreStateFreeSaved();
715 else
716 throw std::runtime_error(
717 "GpuState::RestoreInternalDestructiveSavedState: Invalid simulation "
718 "type for restoring the state destructively.");
719 }
720
729 void SaveState() override {
730 if (simulationType == SimulationType::kStatevector)
731 state->SaveState();
732 else if (simulationType == SimulationType::kMatrixProductState)
733 mps->SaveState();
734 else if (simulationType == SimulationType::kTensorNetwork)
735 tn->SaveState();
736 else if (simulationType == SimulationType::kPauliPropagator)
737 pp->SaveState();
738 }
739
747 void RestoreState() override {
748 if (simulationType == SimulationType::kStatevector)
749 state->RestoreStateNoFreeSaved();
750 else if (simulationType == SimulationType::kMatrixProductState)
751 mps->RestoreState();
752 else if (simulationType == SimulationType::kTensorNetwork)
753 tn->RestoreState();
754 else if (simulationType == SimulationType::kPauliPropagator)
755 pp->RestoreState();
756 }
757
765 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
766 return Amplitude(outcome);
767 }
768
777 void SetMultithreading(bool multithreading = true) override {
778 // don't do anything here, the multithreading is always enabled
779 }
780
788 bool GetMultithreading() const override { return true; }
789
800 bool IsQcsim() const override { return false; }
801
815 Types::qubit_t MeasureNoCollapse() override {
816 if (simulationType == SimulationType::kStatevector)
817 return state->MeasureAllQubitsNoCollapse();
818 else if (simulationType == SimulationType::kMatrixProductState ||
819 simulationType == SimulationType::kTensorNetwork ||
820 simulationType == SimulationType::kPauliPropagator) {
821 Types::qubits_vector fixedValues(nrQubits);
822 std::iota(fixedValues.begin(), fixedValues.end(), 0);
823 const auto res = SampleCounts(fixedValues, 1);
824 if (res.empty()) return 0;
825 return res.begin()
826 ->first; // return the first outcome, as it is the only one
827 }
828
829 throw std::runtime_error(
830 "QCSimState::MeasureNoCollapse: Invalid simulation type for measuring "
831 "all the qubits without collapsing the state.");
832
833 return 0;
834 }
835
836 protected:
837 SimulationType simulationType =
838 SimulationType::kStatevector;
840 std::unique_ptr<GpuLibStateVectorSim>
841 state;
842 std::unique_ptr<GpuLibMPSSim> mps;
843 std::unique_ptr<GpuLibTNSim> tn;
844 std::unique_ptr<GpuPauliPropagator> pp;
846 size_t nrQubits = 0;
847 bool limitSize = false;
848 bool limitEntanglement = false;
849 Eigen::Index chi = 10; // if limitSize is true
850 double singularValueThreshold = 0.; // if limitEntanglement is true
851};
852
853} // namespace Private
854} // namespace Simulators
855
856#endif
857#endif
858#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)