Maestro 0.1.0
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
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 {
39public:
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("GpuState::Initialize: Failed to create "
55 "and initialize the statevector state.");
56 } else
57 throw std::runtime_error(
58 "GpuState::Initialize: Failed to create the statevector state.");
59 } else if (simulationType == SimulationType::kMatrixProductState) {
60 mps = SimulatorsFactory::CreateGpuLibMPSSim();
61 if (mps) {
62 if (limitEntanglement && singularValueThreshold > 0.)
63 mps->SetCutoff(singularValueThreshold);
64 if (limitSize && chi > 0)
65 mps->SetMaxExtent(chi);
66 const bool res = mps->Create(nrQubits);
67 if (!res)
68 throw std::runtime_error("GpuState::Initialize: Failed to create "
69 "and initialize the MPS state.");
70 } else
71 throw std::runtime_error(
72 "GpuState::Initialize: Failed to create the MPS state.");
73 } else
74 throw std::runtime_error("GpuState::Initialize: Invalid simulation "
75 "type for initializing the state.");
76 }
77 }
78
90 void InitializeState(size_t num_qubits,
91 std::vector<std::complex<double>> &amplitudes) override {
92 if (num_qubits == 0)
93 return;
94 Clear();
95 nrQubits = num_qubits;
96 Initialize();
97
98 if (simulationType != SimulationType::kStatevector)
99 throw std::runtime_error("GpuState::InitializeState: Invalid simulation "
100 "type for initializing the state.");
101
102 if (nrQubits) {
103 if (simulationType == SimulationType::kStatevector) {
104 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
105 if (state)
106 state->CreateWithState(
107 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
108 }
109 }
110 }
111
123#ifndef NO_QISKIT_AER
124 void InitializeState(size_t num_qubits,
125 AER::Vector<std::complex<double>> &amplitudes) override {
126 if (num_qubits == 0)
127 return;
128 Clear();
129 nrQubits = num_qubits;
130 Initialize();
131
132 if (simulationType != SimulationType::kStatevector)
133 throw std::runtime_error("GpuState::InitializeState: Invalid simulation "
134 "type for initializing the state.");
135
136 if (nrQubits) {
137 if (simulationType == SimulationType::kStatevector) {
138 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
139 if (state)
140 state->CreateWithState(
141 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
142 }
143 }
144 }
145#endif
146
158 void InitializeState(size_t num_qubits,
159 Eigen::VectorXcd &amplitudes) override {
160 if (num_qubits == 0)
161 return;
162 Clear();
163 nrQubits = num_qubits;
164 Initialize();
165
166 if (simulationType != SimulationType::kStatevector)
167 throw std::runtime_error("GpuState::InitializeState: Invalid simulation "
168 "type for initializing the state.");
169
170 if (nrQubits) {
171 if (simulationType == SimulationType::kStatevector) {
172 state = SimulatorsFactory::CreateGpuLibStateVectorSim();
173 if (state)
174 state->CreateWithState(
175 nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
176 }
177 }
178 }
179
186 void Reset() override {
187 if (state)
188 state->Reset();
189 else if (mps)
190 mps->Reset();
191 }
192
201 void Configure(const char *key, const char *value) override {
202 if (std::string("method") == key) {
203 if (std::string("statevector") == value)
204 simulationType = SimulationType::kStatevector;
205 else if (std::string("matrix_product_state") == value)
206 simulationType = SimulationType::kMatrixProductState;
207 } else if (std::string("matrix_product_state_truncation_threshold") ==
208 key) {
209 singularValueThreshold = std::stod(value);
210 if (singularValueThreshold > 0.) {
211 limitEntanglement = true;
212 if (mps)
213 mps->SetCutoff(singularValueThreshold);
214 } else
215 limitEntanglement = false;
216 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
217 chi = std::stoi(value);
218 if (chi > 0) {
219 limitSize = true;
220 if (mps)
221 mps->SetMaxExtent(chi);
222 } else
223 limitSize = false;
224 }
225 }
226
234 std::string GetConfiguration(const char *key) const override {
235 if (std::string("method") == key) {
236 switch (simulationType) {
237 case SimulationType::kStatevector:
238 return "statevector";
239 case SimulationType::kMatrixProductState:
240 return "matrix_product_state";
241 default:
242 return "other";
243 }
244 } else if (std::string("matrix_product_state_truncation_threshold") ==
245 key) {
246 if (limitEntanglement && singularValueThreshold > 0.)
247 return std::to_string(singularValueThreshold);
248 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
249 if (limitSize && limitSize > 0)
250 return std::to_string(chi);
251 }
252
253 return "";
254 }
255
263 size_t AllocateQubits(size_t num_qubits) override {
264 if ((simulationType == SimulationType::kStatevector && state) ||
265 (simulationType == SimulationType::kMatrixProductState && mps))
266 return 0;
267
268 const size_t oldNrQubits = nrQubits;
269 nrQubits += num_qubits;
270 return oldNrQubits;
271 }
272
279 size_t GetNumberOfQubits() const override { return nrQubits; }
280
288 void Clear() override {
289 state = nullptr;
290 mps = nullptr;
291 nrQubits = 0;
292 }
293
301 size_t Measure(const Types::qubits_vector &qubits) override {
302 // TODO: this is inefficient, maybe implement it better in gpu sim
303 // for now it has the possibility of measuring a qubits interval, but not a
304 // list of qubits
305
306 size_t res = 0;
307 size_t mask = 1ULL;
308
309 DontNotify();
310 if (simulationType == SimulationType::kStatevector) {
311 // TODO: measure all qubits in one shot?
312 for (size_t qubit : qubits) {
313 if (state->MeasureQubitCollapse(static_cast<int>(qubit)))
314 res |= mask;
315 mask <<= 1;
316 }
317 } else if (simulationType == SimulationType::kMatrixProductState) {
318 // TODO: measure all qubits in one shot?
319 for (size_t qubit : qubits) {
320 if (mps->Measure(static_cast<unsigned int>(qubit)))
321 res |= mask;
322 mask <<= 1;
323 }
324 }
325
326 Notify();
327 NotifyObservers(qubits);
328
329 return res;
330 }
331
338 void ApplyReset(const Types::qubits_vector &qubits) override {
339 DontNotify();
340 if (simulationType == SimulationType::kStatevector) {
341 for (size_t qubit : qubits)
342 if (state->MeasureQubitCollapse(static_cast<int>(qubit)))
343 state->ApplyX(static_cast<int>(qubit));
344 } else if (simulationType == SimulationType::kMatrixProductState) {
345 for (size_t qubit : qubits)
346 if (mps->Measure(static_cast<unsigned int>(qubit)))
347 mps->ApplyX(static_cast<unsigned int>(qubit));
348 }
349
350 Notify();
351 NotifyObservers(qubits);
352 }
353
365 double Probability(Types::qubit_t outcome) override {
366 if (simulationType == SimulationType::kStatevector)
367 return state->BasisStateProbability(outcome);
368 else if (simulationType == SimulationType::kMatrixProductState) {
369 const auto ampl = Amplitude(outcome);
370 return std::norm(ampl);
371 }
372
373 return 0.0;
374 }
375
386 std::complex<double> Amplitude(Types::qubit_t outcome) override {
387 double real = 0.0;
388 double imag = 0.0;
389
390 if (simulationType == SimulationType::kStatevector)
391 state->Amplitude(outcome, &real, &imag);
392 else if (simulationType == SimulationType::kMatrixProductState) {
393 std::vector<long int> fixedValues(nrQubits);
394 for (size_t i = 0; i < nrQubits; ++i)
395 fixedValues[i] = (outcome & (1ULL << i)) ? 1 : 0;
396 mps->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
397 }
398
399 return std::complex<double>(real, imag);
400 }
401
412 std::vector<double> AllProbabilities() override {
413 if (nrQubits == 0)
414 return {};
415 const size_t numStates = 1ULL << nrQubits;
416 std::vector<double> result(numStates);
417
418 if (simulationType == SimulationType::kStatevector)
419 state->AllProbabilities(result.data());
420 else if (simulationType == SimulationType::kMatrixProductState) {
421 // this is very slow, it should be used only for tests!
422 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
423 const auto val = Amplitude(i);
424 result[i] = std::norm(std::complex<double>(val.real(), val.imag()));
425 }
426 }
427
428 return result;
429 }
430
442 std::vector<double>
443 Probabilities(const Types::qubits_vector &qubits) override {
444 std::vector<double> result(qubits.size());
445
446 if (simulationType == SimulationType::kStatevector) {
447 for (size_t i = 0; i < qubits.size(); ++i)
448 result[i] = state->BasisStateProbability(qubits[i]);
449 } else if (simulationType == SimulationType::kMatrixProductState) {
450 for (size_t i = 0; i < qubits.size(); ++i) {
451 const auto ampl = Amplitude(qubits[i]);
452 result[i] = std::norm(ampl);
453 }
454 }
455
456 return result;
457 }
458
472 std::unordered_map<Types::qubit_t, Types::qubit_t>
474 size_t shots = 1000) override {
475 if (qubits.empty() || shots == 0)
476 return {};
477
478 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
479
480 DontNotify();
481
482 if (simulationType == SimulationType::kStatevector) {
483 std::vector<long int> samples(shots);
484 state->SampleAll(shots, samples.data());
485
486 for (auto outcome : samples)
487 ++result[outcome];
488 } else if (simulationType == SimulationType::kMatrixProductState) {
489 std::unordered_map<std::vector<bool>, int64_t> *map =
490 mps->GetMapForSample();
491
492 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
493
494 mps->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
495
496 // put the results in the result map
497 for (const auto &[meas, cnt] : *map) {
498 Types::qubit_t outcome = 0;
499 Types::qubit_t mask = 1ULL;
500 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
501 if (meas[q])
502 outcome |= mask;
503 mask <<= 1;
504 }
505
506 result[outcome] += cnt;
507 }
508
509 mps->FreeMapForSample(map);
510 }
511
512 Notify();
513 NotifyObservers(qubits);
514
515 return result;
516 }
517
529 double ExpectationValue(const std::string &pauliString) override {
530 double result = 0.0;
531
532 if (simulationType == SimulationType::kStatevector)
533 result = state->ExpectationValue(pauliString);
534 else if (simulationType == SimulationType::kMatrixProductState)
535 result = mps->ExpectationValue(pauliString);
536
537 return result;
538 }
539
547 SimulatorType GetType() const override { return SimulatorType::kGpuSim; }
548
557 SimulationType GetSimulationType() const override { return simulationType; }
558
567 void Flush() override {}
568
579 void SaveStateToInternalDestructive() override {
580 if (simulationType == SimulationType::kStatevector)
581 state->SaveStateDestructive();
582 else
583 throw std::runtime_error(
584 "GpuState::SaveStateToInternalDestructive: Invalid simulation type "
585 "for saving the state destructively.");
586 }
587
595 if (simulationType == SimulationType::kStatevector)
596 state->RestoreStateFreeSaved();
597 else
598 throw std::runtime_error(
599 "GpuState::RestoreInternalDestructiveSavedState: Invalid simulation "
600 "type for restoring the state destructively.");
601 }
602
611 void SaveState() override {
612 if (simulationType == SimulationType::kStatevector)
613 state->SaveState();
614 else if (simulationType == SimulationType::kMatrixProductState)
615 mps->SaveState();
616 }
617
625 void RestoreState() override {
626 if (simulationType == SimulationType::kStatevector)
627 state->RestoreStateNoFreeSaved();
628 else if (simulationType == SimulationType::kMatrixProductState)
629 mps->RestoreState();
630 }
631
639 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
640 return Amplitude(outcome);
641 }
642
651 void SetMultithreading(bool multithreading = true) override {
652 // don't do anything here, the multithreading is always enabled
653 }
654
662 bool GetMultithreading() const override { return true; }
663
674 bool IsQcsim() const override { return false; }
675
690 if (simulationType == SimulationType::kStatevector)
691 return state->MeasureAllQubitsNoCollapse();
692 else if (simulationType == SimulationType::kMatrixProductState) {
693 Types::qubits_vector fixedValues(nrQubits);
694 std::iota(fixedValues.begin(), fixedValues.end(), 0);
695 const auto res = SampleCounts(fixedValues, 1);
696 if (res.empty())
697 return 0;
698 return res.begin()
699 ->first; // return the first outcome, as it is the only one
700 }
701
702 throw std::runtime_error(
703 "QCSimState::MeasureNoCollapse: Invalid simulation type for measuring "
704 "all the qubits without collapsing the state.");
705
706 return 0;
707 }
708
709protected:
710 SimulationType simulationType =
711 SimulationType::kStatevector;
712
713 std::unique_ptr<GpuLibStateVectorSim>
714 state;
715 std::unique_ptr<GpuLibMPSSim> mps;
716
717 size_t nrQubits = 0;
718 bool limitSize = false;
719 bool limitEntanglement = false;
720 Eigen::Index chi = 10; // if limitSize is true
721 double singularValueThreshold = 0.; // if limitEntanglement is true
722};
723
724} // namespace Private
725} // namespace Simulators
726
727#endif
728#endif
729#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)
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:21
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:20