Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
ImmediateGpuSimulator.h
Go to the documentation of this file.
1#pragma once
2#if defined(INCLUDED_BY_FACTORY) && defined(__linux__)
7#include <algorithm>
8#include <cctype>
9#include <functional>
10#include <iomanip>
11#include <limits>
12#include <random>
13#include <sstream>
14#include <utility>
15
16namespace Simulators::Private
17{
18// Common configuration, sampling and circuit bookkeeping. Native state belongs
19// exclusively to the concrete simulator selected by the factory.
20class ImmediateGpuSimulator : public ISimulator
21{
22 public:
23 virtual const char *MethodName() const = 0;
24
25 protected:
26 void ValidateMethod(const char *value) const
27 {
28 if (std::string(value) != MethodName())
29 throw std::invalid_argument("A concrete simulator cannot change method; select the backend through SimulatorsFactory");
30 }
31
32 virtual void RefreshRoutingCallback()
33 {
34 }
35
36 public:
37 void InitializeState(size_t num_qubits, std::vector<std::complex<double>> &amplitudes) override
38 {
39 if (num_qubits == 0)
40 return;
41 Clear();
42 nrQubits = num_qubits;
43 Initialize();
44 throw std::runtime_error("GpuState::InitializeState: Invalid simulation "
45 "type for initializing the state.");
46 }
47
48#ifndef NO_QISKIT_AER
49 void InitializeState(size_t num_qubits, AER::Vector<std::complex<double>> &amplitudes) override
50 {
51 if (num_qubits == 0)
52 return;
53 Clear();
54 nrQubits = num_qubits;
55 Initialize();
56 throw std::runtime_error("GpuState::InitializeState: Invalid simulation "
57 "type for initializing the state.");
58 }
59
60#endif
61
62 void InitializeState(size_t num_qubits, Eigen::VectorXcd &amplitudes) override
63 {
64 if (num_qubits == 0)
65 return;
66 Clear();
67 nrQubits = num_qubits;
68 Initialize();
69 throw std::runtime_error("GpuState::InitializeState: Invalid simulation "
70 "type for initializing the state.");
71 }
72
73 void InitializeToBasisState(size_t num_qubits, Types::qubit_t basisState) override
74 {
75 if (num_qubits == 0)
76 return;
77 Clear();
78 nrQubits = num_qubits;
79 Initialize();
80 for (size_t q = 0; q < num_qubits; ++q)
81 if ((basisState >> q) & 1ULL)
82 ApplyX(static_cast<Types::qubit_t>(q));
83 }
84
85 void InitializeToBasisState(size_t num_qubits, const std::vector<bool> &basisState) override
86 {
87 if (num_qubits == 0)
88 return;
89 Clear();
90 nrQubits = num_qubits;
91 Initialize();
92 for (size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
93 if (basisState[q])
94 ApplyX(static_cast<Types::qubit_t>(q));
95 }
96
97 void InitializeToMixtureOfBasisStates(size_t num_qubits, const std::vector<std::pair<Types::qubit_t, double>> &mixture) override
98 {
99 if (num_qubits == 0)
100 return;
101 Clear();
102 nrQubits = num_qubits;
103 Initialize();
104 throw std::runtime_error("GpuState::InitializeToMixtureOfBasisStates: Invalid simulation "
105 "type for initializing to a mixture of basis states.");
106 }
107
108 void InitializeToMixtureOfBasisStates(size_t num_qubits, const std::vector<std::pair<std::vector<bool>, double>> &mixture) override
109 {
110 if (num_qubits == 0)
111 return;
112 Clear();
113 nrQubits = num_qubits;
114 Initialize();
115 throw std::runtime_error("GpuState::InitializeToMixtureOfBasisStates: Invalid simulation "
116 "type for initializing to a mixture of basis states.");
117 }
118
119 bool SupportsMPSSwapOptimization() const override
120 {
121 return true;
122 }
123
124 void SetUpcomingGates(const std::vector<std::shared_ptr<Circuits::IOperation<double>>> &gates) override
125 {
126 upcomingGates = gates;
127 upcomingGateIndex = 0;
128 if (!gateCounterObserver)
129 gateCounterObserver = std::make_shared<GateCounterObserver>(upcomingGateIndex);
130 RegisterObserver(gateCounterObserver);
131 RefreshRoutingCallback();
132 }
133
134 long long int GetGatesCounter() const override
135 {
136 return upcomingGateIndex;
137 }
138
139 void SetGatesCounter(long long int counter) override
140 {
141 upcomingGateIndex = counter;
142 }
143
144 void IncrementGatesCounter() override
145 {
146 ++upcomingGateIndex;
147 }
148
149 size_t GetNumberOfQubits() const override
150 {
151 return nrQubits;
152 }
153
154 bool SupportsQuantumChannels() const override
155 {
156 return false;
157 }
158
159 void ApplyQuantumChannel(const Types::qubits_vector &targets, const QuantumChannel &channel) override
160 {
161 throw std::runtime_error("GPU quantum channels require an initialized density matrix or "
162 "matrix product operator");
163 }
164
165 double ProbabilityBits(const std::vector<bool> &bits) override
166 {
167 return IState::ProbabilityBits(bits);
168 }
169
170 std::complex<double> DensityMatrixElementBits(const std::vector<bool> &row, const std::vector<bool> &col) const override
171 {
172 return IState::DensityMatrixElementBits(row, col);
173 }
174
175 Eigen::MatrixXcd GetDensityMatrix(bool normalized = true) const override
176 {
177 return IState::GetDensityMatrix(normalized);
178 }
179
180 std::complex<double> ExpectationValueComplex(const std::string &pauli, bool normalized = true) const override
181 {
182 return IState::ExpectationValueComplex(pauli, normalized);
183 }
184
185 std::vector<std::complex<double>> ExpectationValuesComplex(const std::vector<std::string> &paulis, bool normalized = true) const override
186 {
187 return IState::ExpectationValuesComplex(paulis, normalized);
188 }
189
190 std::complex<double> ExpectationValueOperators(const Types::qubits_vector &qubits, const std::vector<Eigen::MatrixXcd> &matrices) override
191 {
192 return IState::ExpectationValueOperators(qubits, matrices);
193 }
194
195 void ApplyOperator(const Types::qubits_vector &qubits, const Eigen::MatrixXcd &matrix, bool normalize = false) override
196 {
197 return IState::ApplyOperator(qubits, matrix, normalize);
198 }
199
200 void MoveAtBeginningOfChain(const Types::qubits_vector &qubits) override
201 {
202 return IState::MoveAtBeginningOfChain(qubits);
203 }
204
205 std::vector<std::complex<double>> GetStateVector() override
206 {
207 return IState::GetStateVector();
208 }
209
210 std::complex<double> DensityMatrixTrace() const override
211 {
212 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
213 "matrix_product_operator");
214 }
215
216 std::complex<double> DensityMatrixOverlap(const IState &other) const override
217 {
218 throw std::invalid_argument("Density-matrix overlap requires two density matrices or two MPOs");
219 }
220
221 double DensityMatrixPurity() const override
222 {
223 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
224 "matrix_product_operator");
225 }
226
227 std::complex<double> DensityMatrixTraceOfSquare() const override
228 {
229 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
230 "matrix_product_operator");
231 }
232
233 double DensityMatrixHermiticityResidual() const override
234 {
235 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
236 "matrix_product_operator");
237 }
238
239 bool IsDensityMatrixHermitian(double eps = 1e-10) const override
240 {
241 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
242 "matrix_product_operator");
243 }
244
245 Eigen::MatrixXcd PartialTrace(const Types::qubits_vector &qubits) const override
246 {
247 std::vector<int> keep(qubits.begin(), qubits.end());
248 throw std::runtime_error("GPU partial trace requires a mixed-state backend");
249 }
250
251 double FidelityWithStatevector(const Eigen::VectorXcd &psi) const override
252 {
253 std::vector<double> raw(2 * static_cast<size_t>(psi.size()));
254 for (Eigen::Index i = 0; i < psi.size(); ++i)
255 {
256 raw[2 * i] = psi[i].real();
257 raw[2 * i + 1] = psi[i].imag();
258 }
259 throw std::runtime_error("GPU mixed-state fidelity requires density_matrix or "
260 "matrix_product_operator");
261 }
262
263 void RestoreDensityMatrixTrace() override
264 {
265 throw std::runtime_error("Trace restoration is only available for GPU MPO");
266 }
267
268 void HermitizeDensityMatrix() override
269 {
270 throw std::runtime_error("Hermitization is only available for GPU MPO");
271 }
272
273 void Trim() override
274 {
275 throw std::runtime_error("Trim is only available for GPU MPS and MPO");
276 }
277
278 void ReCanonicalize() override
279 {
280 throw std::runtime_error("Canonicalization is only available for GPU MPS and MPO");
281 }
282
283 double Probability(Types::qubit_t outcome) override
284 {
285 const auto ampl = Amplitude(outcome);
286 return std::norm(ampl);
287 }
288
289 std::complex<double> ProjectOnZero() override
290 {
291 return Amplitude(0);
292 }
293
294 std::vector<double> Probabilities(const Types::qubits_vector &qubits) override
295 {
296 std::vector<double> result(qubits.size());
297 {
298 for (size_t i = 0; i < qubits.size(); ++i)
299 {
300 const auto ampl = Amplitude(qubits[i]);
301 result[i] = std::norm(ampl);
302 }
303 }
304 return result;
305 }
306
307 std::vector<double> ExpectationValues(const std::vector<std::string> &paulis) override
308 {
309 return IState::ExpectationValues(paulis);
310 }
311
312 SimulatorType GetType() const override
313 {
314 return SimulatorType::kGpuSim;
315 }
316
317 void Flush() override
318 {
319 const bool done = true;
320 if (!done)
321 throw std::runtime_error("GpuState::Flush: device synchronization failed");
322 }
323
324 void SaveStateToInternalDestructive() override
325 {
326 throw std::runtime_error("GpuState::SaveStateToInternalDestructive: Invalid simulation type "
327 "for saving the state destructively.");
328 }
329
331 {
332 throw std::runtime_error("GpuState::RestoreInternalDestructiveSavedState: Invalid simulation "
333 "type for restoring the state destructively.");
334 }
335
336 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override
337 {
338 return Amplitude(outcome);
339 }
340
341 void SetMultithreading(bool multithreading = true) override
342 {
343 }
344
345 bool GetMultithreading() const override
346 {
347 return true;
348 }
349
350 bool IsQcsim() const override
351 {
352 return false;
353 }
354
356 {
357 if (nrQubits > sizeof(Types::qubit_t) * 8)
358 std::cerr << "Warning: The number of qubits to measure is larger than the "
359 "number of bits in the Types::qubit_t type, the outcome will be "
360 "undefined"
361 << std::endl;
362 Types::qubits_vector fixedValues(nrQubits);
363 std::iota(fixedValues.begin(), fixedValues.end(), 0);
364 const auto res = SampleCounts(fixedValues, 1);
365 if (res.empty())
366 return 0;
367 return res.begin()->first;
368 }
369
370 std::vector<bool> MeasureNoCollapseMany() override
371 {
372 Types::qubits_vector fixedValues(nrQubits);
373 std::iota(fixedValues.begin(), fixedValues.end(), 0);
374 const auto res = SampleCountsMany(fixedValues, 1);
375 if (res.empty())
376 return std::vector<bool>(nrQubits, false);
377 return res.begin()->first;
378 }
379
380 const Configuration &GetConfiguration() const
381 {
382 return configuration;
383 }
384
385 const std::unordered_map<std::string, std::string> &GetConfigMap() const override
386 {
387 return configuration.GetConfigMap();
388 }
389
390 void ApplyGenericOneQubitGate(Types::qubit_t qubit, const Eigen::Matrix2cd &gate) override
391 {
392 throw std::runtime_error("GpuSimulator::ApplyGenericOneQubitGate: Not supported for GPU "
393 "simulator yet.");
394 }
395
396 void ApplyGenericTwoQubitGate(Types::qubit_t qubit0, Types::qubit_t qubit1, const Eigen::Matrix4cd &gate) override
397 {
398 throw std::runtime_error("GpuSimulator::ApplyGenericTwoQubitGate: Not supported for GPU "
399 "simulator yet.");
400 }
401
403 const Eigen::Matrix<std::complex<double>, 8, 8> &gate) override
404 {
405 throw std::runtime_error("Generic three-qubit gates require GPU statevector");
406 }
407
408 void ApplyNop() override
409 {
410 }
411
412 protected:
413 static std::vector<size_t> SampleBitPositions(const Types::qubits_vector &qubits)
414 {
415 auto sorted = qubits;
416 std::sort(sorted.begin(), sorted.end());
417 sorted.erase(std::unique(sorted.begin(), sorted.end()), sorted.end());
418 std::vector<size_t> positions;
419 positions.reserve(qubits.size());
420 for (const auto q : qubits)
421 positions.push_back(std::lower_bound(sorted.begin(), sorted.end(), q) - sorted.begin());
422 return positions;
423 }
424
425 uint64_t nextSeedStream = 0;
426
427 size_t nrQubits = 0;
428
429 std::vector<std::shared_ptr<Circuits::IOperation<>>> upcomingGates;
430
431 long long int upcomingGateIndex = 0;
432
433 class GateCounterObserver : public ISimulatorObserver
434 {
435 public:
436 GateCounterObserver(long long int &indexRef) : index(indexRef)
437 {
438 }
439
440 void Update(const Types::qubits_vector &) override
441 {
442 ++index;
443 }
444
445 private:
446 long long int &index;
447 };
448
449 std::shared_ptr<GateCounterObserver> gateCounterObserver;
450
451 Configuration configuration;
452};
453} // namespace Simulators::Private
454#endif
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
int ApplyX(void *sim, int qubit)
unsigned long int GetNumberOfQubits(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int GetMultithreading(void *sim)
double * Amplitude(void *sim, unsigned long long int outcome)
int ApplyGenericThreeQubitGate(void *sim, unsigned long q0, unsigned long q1, unsigned long q2, const double *buffer)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int SetMultithreading(void *sim, int multithreading)
int SaveStateToInternalDestructive(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 ApplyGenericOneQubitGate(void *sim, unsigned long q0, const double *buffer)
int ApplyGenericTwoQubitGate(void *sim, unsigned long q0, unsigned long q1, const double *buffer)
int IsQcsim(void *sim)
SimulatorType
The type of simulator.
Definition State.h:79
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:24
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:23