Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuPauliPropagatorSimulator.h
Go to the documentation of this file.
1#pragma once
2#if defined(INCLUDED_BY_FACTORY) && defined(__linux__)
3#include "../Core/Factory.h"
5
7{
8// pauli_propagator backend. Owns exactly one native Gpu implementation.
9class GpuPauliPropagatorSimulator : public ImmediateGpuSimulator
10{
11 public:
12 GpuPauliPropagatorSimulator()
13 {
14 configuration.SetConfiguration("method", "pauli_propagator");
15 }
16
17 const char *MethodName() const override
18 {
19 return "pauli_propagator";
20 }
21
22 void Initialize() override
23 {
24 if (nrQubits)
25 {
26 auto initializationLock = GpuLibrary::GetInstance()->LockInitialization();
27 const int gpuDevice = configuration.IsSet("gpu_device") ? Configuration::ParseGpuDevice(configuration.GetConfiguration("gpu_device"))
28 : SimulatorsFactory::ResolveGpuDevice();
29 configuration.SetConfiguration("gpu_device", std::to_string(gpuDevice));
30 if (!SimulatorsFactory::GetGpuLibrary(gpuDevice))
31 throw std::runtime_error("GpuState::Initialize: Unable to initialize GPU device " + std::to_string(gpuDevice));
32 {
33 pp = SimulatorsFactory::CreateGpuPauliPropagatorSimulatorUnique(gpuDevice);
34 if (pp)
35 {
36 const bool res = pp->CreateSimulator(nrQubits);
37 if (!res)
38 throw std::runtime_error("GpuState::Initialize: Failed to create "
39 "and initialize the Pauli propagator state.");
40 for (const auto &[key, value] : configuration.GetConfigMap())
41 if (key != "method")
42 Configure(key.c_str(), value.c_str());
43 pp->SetWillUseSampling(true);
44 if (!pp->AllocateMemory(0.9))
45 throw std::runtime_error("GpuState::Initialize: Failed to allocate memory for the "
46 "Pauli propagator state.");
47 }
48 else
49 throw std::runtime_error("GpuState::Initialize: Failed to create the Pauli propagator "
50 "state.");
51 }
52 if (GetGpuDevice() != gpuDevice)
53 throw std::runtime_error("GpuState::Initialize: GPU plugin did not confirm the requested "
54 "device; update the GPU library");
55 }
56 }
57
58 void Reset() override
59 {
60 if (pp)
61 pp->ClearOperators();
62 upcomingGateIndex = 0;
63 }
64
65 void Configure(const char *key, const char *value) override
66 {
67 if (!key || !value)
68 return;
69 if (std::string("gpu_device") == key)
70 {
71 const int device = Configuration::ParseGpuDevice(value);
72 if ((pp) && device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
73 throw std::invalid_argument("gpu_device cannot change after initialization; clear the "
74 "simulator first");
75 configuration.SetConfiguration(key, std::to_string(device));
76 return;
77 }
78 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
79 if (!svdGroup.empty())
80 {
81 Configuration::ParseGpuSvdFlag(value);
82 configuration.SetConfiguration(key, value);
83 return;
84 }
85 if (std::string("method") == key)
86 {
87 ValidateMethod(value);
88 configuration.SetConfiguration(key, value);
89 return;
90 }
91 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
92 {
93 Configuration::ParsePrecision(key, value);
94 configuration.SetConfiguration(key, value);
95 return;
96 }
97 if (!configuration.WasApplied(key, value))
98 configuration.SetConfiguration(key, value);
99 if (std::string("seed") == key)
100 {
101 const uint64_t seed = std::stoull(value);
102 SeedAuxiliaryRng(seed);
103 nextSeedStream = 0;
104 if (pp)
105 pp->SetSeed(seed);
106 return;
107 }
108 // Configuration can be replayed from another method. Preserve the
109 // numeric validation, but tensor truncation has no native effect here.
110 if (std::string("matrix_product_state_truncation_threshold") == key || std::string("matrix_product_operator_truncation_threshold") == key)
111 std::stod(value);
112 else if (std::string("matrix_product_state_max_bond_dimension") == key || std::string("matrix_product_operator_max_bond_dimension") == key)
113 std::stoi(value);
114
115 if (pp)
116 {
117 if (std::string("pauli_propagator_coefficient_threshold") == key)
118 {
119 const double coefficientThreshold = std::stod(value);
120 pp->SetCoefficientTruncationCutoff(coefficientThreshold);
121 }
122 else if (std::string("pauli_propagator_pauli_weight_threshold") == key)
123 {
124 const double pauliWeightThreshold = std::stod(value);
125 pp->SetWeightTruncationCutoff(pauliWeightThreshold);
126 }
127 else if (std::string("pauli_propagator_steps_between_trims") == key)
128 {
129 const int stepsBetweenTrims = std::stoi(value);
130 pp->SetNumGatesBetweenTruncations(stepsBetweenTrims);
131 }
132 else if (std::string("pauli_propagator_num_gates_between_deduplications") == key)
133 {
134 const int numGatesBetweenDeduplications = std::stoi(value);
135 pp->SetNumGatesBetweenDeduplications(numGatesBetweenDeduplications);
136 if (!configuration.IsSet("pauli_propagator_steps_between_trims"))
137 pp->SetNumGatesBetweenTruncations(numGatesBetweenDeduplications);
138 }
139 }
140 }
141
142 std::string GetConfiguration(const char *key) const override
143 {
144 if (!key)
145 return {};
146 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
147 {
148 const bool useDouble = configuration.GetConfiguration("precision") == "double";
149 return std::string(key) == "precision" ? (useDouble ? "double" : "single") : (useDouble ? "1" : "0");
150 }
151 if (!key)
152 return {};
153 if (std::string("method") == key)
154 return MethodName();
155 return configuration.GetConfiguration(key);
156 }
157
158 size_t AllocateQubits(size_t num_qubits) override
159 {
160 if (pp)
161 return 0;
162 const size_t oldNrQubits = nrQubits;
163 nrQubits += num_qubits;
164 return oldNrQubits;
165 }
166
167 void Clear() override
168 {
169 pp = nullptr;
170 nrQubits = 0;
171 upcomingGateIndex = 0;
172 upcomingGates.clear();
173 }
174
175 size_t Measure(const Types::qubits_vector &qubits) override
176 {
177 if (qubits.size() > sizeof(size_t) * 8)
178 std::cerr << "Warning: The number of qubits to measure is larger than the "
179 "number of bits in the size_t type, the outcome will be undefined"
180 << std::endl;
181 size_t res = 0;
182 size_t mask = 1ULL;
183 DontNotify();
184 {
185 for (size_t qubit : qubits)
186 {
187 if (pp->MeasureQubit(static_cast<int>(qubit)))
188 res |= mask;
189 mask <<= 1;
190 }
191 }
192 Notify();
193 NotifyObservers(qubits);
194 return res;
195 }
196
197 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override
198 {
199 std::vector<bool> res(qubits.size(), false);
200 DontNotify();
201 {
202 for (size_t i = 0; i < qubits.size(); ++i)
203 res[i] = pp->MeasureQubit(static_cast<int>(qubits[i]));
204 }
205 Notify();
206 NotifyObservers(qubits);
207 return res;
208 }
209
210 void ApplyReset(const Types::qubits_vector &qubits) override
211 {
212 DontNotify();
213 {
214 for (size_t qubit : qubits)
215 if (pp->MeasureQubit(static_cast<int>(qubit)))
216 pp->ApplyX(static_cast<int>(qubit));
217 }
218 Notify();
219 NotifyObservers(qubits);
220 }
221
222 double Probability(Types::qubit_t outcome) override
223 {
224 return pp->Probability(outcome);
225 }
226
227 std::complex<double> Amplitude(Types::qubit_t outcome) override
228 {
229 {
230 throw std::runtime_error("GpuState::Amplitude: Invalid simulation type for amplitude "
231 "calculation.");
232 }
233 }
234
235 std::vector<double> AllProbabilities() override
236 {
237 if (nrQubits == 0)
238 return {};
239 if (nrQubits >= std::numeric_limits<size_t>::digits)
240 throw std::length_error("Full probability enumeration exceeds the basis-index API; query "
241 "selected bit vectors");
242 const size_t numStates = size_t{1} << nrQubits;
243 std::vector<double> result(numStates);
244 {
245 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i)
246 {
247 result[i] = pp->Probability(i);
248 }
249 }
250 return result;
251 }
252
253 std::vector<double> Probabilities(const Types::qubits_vector &qubits) override
254 {
255 std::vector<double> result(qubits.size());
256 {
257 for (size_t i = 0; i < qubits.size(); ++i)
258 result[i] = pp->Probability(qubits[i]);
259 }
260 return result;
261 }
262
263 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(const Types::qubits_vector &qubits, size_t shots = 1000) override
264 {
265 if (qubits.empty() || shots == 0)
266 return {};
267 if (qubits.size() > sizeof(Types::qubit_t) * 8)
268 std::cerr << "Warning: The number of qubits to measure is larger than the "
269 "number of bits in the Types::qubit_t type, the outcome will be "
270 "undefined"
271 << std::endl;
272 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
273 DontNotify();
274 {
275 std::vector<int> qb(qubits.begin(), qubits.end());
276 for (size_t shot = 0; shot < shots; ++shot)
277 {
278 size_t meas = 0;
279 auto res = pp->SampleQubits(qb);
280 for (size_t i = 0; i < qubits.size(); ++i)
281 {
282 if (res[i])
283 meas |= (1ULL << i);
284 }
285 ++result[meas];
286 }
287 }
288 Notify();
289 NotifyObservers(qubits);
290 return result;
291 }
292
293 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(const Types::qubits_vector &qubits, size_t shots = 1000) override
294 {
295 if (qubits.empty() || shots == 0)
296 return {};
297 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
298 DontNotify();
299 {
300 std::vector<int> qb(qubits.begin(), qubits.end());
301 for (size_t shot = 0; shot < shots; ++shot)
302 {
303 const auto res = pp->SampleQubits(qb);
304 ++result[res];
305 }
306 }
307 Notify();
308 NotifyObservers(qubits);
309 return result;
310 }
311
312 double ExpectationValue(const std::string &pauliString) override
313 {
314 double result = 0.0;
315 result = pp->ExpectationValue(pauliString);
316 return result;
317 }
318
319 int GetGpuDevice() const override
320 {
321 if (pp)
322 return pp->GetGpuDevice();
323 return -1;
324 }
325
326 SimulationType GetSimulationType() const override
327 {
328 return SimulationType::kPauliPropagator;
329 }
330
331 void SaveStateToInternalDestructive() override
332 {
333 return;
334 }
335
337 {
338 return;
339 }
340
341 void SaveState() override
342 {
343 pp->SaveState();
344 }
345
346 void RestoreState() override
347 {
348 pp->RestoreState();
349 }
350
351 void ApplyP(Types::qubit_t qubit, double lambda) override
352 {
353 pp->ApplyP(qubit, lambda);
354 NotifyObservers({qubit});
355 }
356
357 void ApplyX(Types::qubit_t qubit) override
358 {
359 pp->ApplyX(qubit);
360 NotifyObservers({qubit});
361 }
362
363 void ApplyY(Types::qubit_t qubit) override
364 {
365 pp->ApplyY(qubit);
366 NotifyObservers({qubit});
367 }
368
369 void ApplyZ(Types::qubit_t qubit) override
370 {
371 pp->ApplyZ(qubit);
372 NotifyObservers({qubit});
373 }
374
375 void ApplyH(Types::qubit_t qubit) override
376 {
377 pp->ApplyH(qubit);
378 NotifyObservers({qubit});
379 }
380
381 void ApplyS(Types::qubit_t qubit) override
382 {
383 pp->ApplyS(qubit);
384 NotifyObservers({qubit});
385 }
386
387 void ApplySDG(Types::qubit_t qubit) override
388 {
389 pp->ApplySDG(qubit);
390 NotifyObservers({qubit});
391 }
392
393 void ApplyT(Types::qubit_t qubit) override
394 {
395 pp->ApplyT(qubit);
396 NotifyObservers({qubit});
397 }
398
399 void ApplyTDG(Types::qubit_t qubit) override
400 {
401 pp->ApplyTDG(qubit);
402 NotifyObservers({qubit});
403 }
404
405 void ApplySx(Types::qubit_t qubit) override
406 {
407 pp->ApplySQRTX(qubit);
408 NotifyObservers({qubit});
409 }
410
411 void ApplySxDAG(Types::qubit_t qubit) override
412 {
413 pp->ApplySxDAG(qubit);
414 NotifyObservers({qubit});
415 }
416
417 void ApplyK(Types::qubit_t qubit) override
418 {
419 pp->ApplyK(qubit);
420 NotifyObservers({qubit});
421 }
422
423 void ApplyRx(Types::qubit_t qubit, double theta) override
424 {
425 pp->ApplyRX(qubit, theta);
426 NotifyObservers({qubit});
427 }
428
429 void ApplyRy(Types::qubit_t qubit, double theta) override
430 {
431 pp->ApplyRY(qubit, theta);
432 NotifyObservers({qubit});
433 }
434
435 void ApplyRz(Types::qubit_t qubit, double theta) override
436 {
437 pp->ApplyRZ(qubit, theta);
438 NotifyObservers({qubit});
439 }
440
441 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda, double gamma) override
442 {
443 pp->ApplyU(qubit, theta, phi, lambda, gamma);
444 NotifyObservers({qubit});
445 }
446
447 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
448 {
449 pp->ApplyCX(ctrl_qubit, tgt_qubit);
450 NotifyObservers({tgt_qubit, ctrl_qubit});
451 }
452
453 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
454 {
455 pp->ApplyCY(ctrl_qubit, tgt_qubit);
456 NotifyObservers({tgt_qubit, ctrl_qubit});
457 }
458
459 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
460 {
461 pp->ApplyCZ(ctrl_qubit, tgt_qubit);
462 NotifyObservers({tgt_qubit, ctrl_qubit});
463 }
464
465 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double lambda) override
466 {
467 pp->ApplyCP(ctrl_qubit, tgt_qubit, lambda);
468 NotifyObservers({tgt_qubit, ctrl_qubit});
469 }
470
471 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
472 {
473 pp->ApplyCRX(ctrl_qubit, tgt_qubit, theta);
474 NotifyObservers({tgt_qubit, ctrl_qubit});
475 }
476
477 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
478 {
479 pp->ApplyCRY(ctrl_qubit, tgt_qubit, theta);
480 NotifyObservers({tgt_qubit, ctrl_qubit});
481 }
482
483 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
484 {
485 pp->ApplyCRZ(ctrl_qubit, tgt_qubit, theta);
486 NotifyObservers({tgt_qubit, ctrl_qubit});
487 }
488
489 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
490 {
491 pp->ApplyCH(ctrl_qubit, tgt_qubit);
492 NotifyObservers({tgt_qubit, ctrl_qubit});
493 }
494
495 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
496 {
497 pp->ApplyCSX(ctrl_qubit, tgt_qubit);
498 NotifyObservers({tgt_qubit, ctrl_qubit});
499 }
500
501 void ApplyCSxDAG(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
502 {
503 pp->ApplyCSXDAG(ctrl_qubit, tgt_qubit);
504 NotifyObservers({tgt_qubit, ctrl_qubit});
505 }
506
507 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override
508 {
509 pp->ApplySWAP(qubit0, qubit1);
510 NotifyObservers({qubit1, qubit0});
511 }
512
513 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1, Types::qubit_t qubit2) override
514 {
515 pp->ApplyCCX(qubit0, qubit1, qubit2);
516 NotifyObservers({qubit0, qubit1, qubit2});
517 }
518
519 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0, Types::qubit_t qubit1) override
520 {
521 pp->ApplyCSwap(ctrl_qubit, qubit0, qubit1);
522 NotifyObservers({qubit1, qubit0, ctrl_qubit});
523 }
524
525 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta, double phi, double lambda, double gamma) override
526 {
527 pp->ApplyCU(ctrl_qubit, tgt_qubit, theta, phi, lambda, gamma);
528 NotifyObservers({tgt_qubit, ctrl_qubit});
529 }
530
531 std::unique_ptr<ISimulator> Clone() override
532 {
533 throw std::runtime_error("GpuSimulator::Clone: Cloning Tensor Network or Pauli Propagator "
534 "simulation is not "
535 "supported.");
536 }
537
538 protected:
539 std::unique_ptr<GpuPauliPropagator> pp;
540};
541} // namespace Simulators::Private
542#endif
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
double * AllProbabilities(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyCSwap(void *sim, int controlQubit, int qubit1, int qubit2)
int ApplyCCX(void *sim, int controlQubit1, int controlQubit2, int targetQubit)
int ApplyY(void *sim, int qubit)
double * Amplitude(void *sim, unsigned long long int outcome)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int SaveStateToInternalDestructive(void *sim)
int ApplyCH(void *sim, int controlQubit, int targetQubit)
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 ApplyCZ(void *sim, int controlQubit, int targetQubit)
int ApplyRz(void *sim, int qubit, double theta)
int RestoreInternalDestructiveSavedState(void *sim)
int ApplyT(void *sim, int qubit)
int ApplyCRx(void *sim, int controlQubit, int targetQubit, double theta)
int SaveState(void *sim)
SimulationType
The type of simulation.
Definition State.h:109
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