Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuTensorNetworkSimulator.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// tensor_network backend. Owns exactly one native Gpu implementation.
9class GpuTensorNetworkSimulator : public ImmediateGpuSimulator
10{
11 public:
12 GpuTensorNetworkSimulator()
13 {
14 configuration.SetConfiguration("method", "tensor_network");
15 }
16
17 const char *MethodName() const override
18 {
19 return "tensor_network";
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 tn = SimulatorsFactory::CreateGpuLibTensorNetSim(gpuDevice);
34 if (tn)
35 {
36 for (const auto &[key, value] : configuration.GetConfigMap())
37 if (key != "method")
38 Configure(key.c_str(), value.c_str());
39 const bool res = tn->Create(nrQubits);
40 if (!res)
41 throw std::runtime_error("GpuState::Initialize: Failed to create "
42 "and initialize the tensor network state.");
43 }
44 else
45 throw std::runtime_error("GpuState::Initialize: Failed to create the tensor network "
46 "state.");
47 }
48 if (GetGpuDevice() != gpuDevice)
49 throw std::runtime_error("GpuState::Initialize: GPU plugin did not confirm the requested "
50 "device; update the GPU library");
51 }
52 }
53
54 void Reset() override
55 {
56 if (tn)
57 tn->Reset();
58 upcomingGateIndex = 0;
59 }
60
61 void Configure(const char *key, const char *value) override
62 {
63 if (!key || !value)
64 return;
65 if (std::string("gpu_device") == key)
66 {
67 const int device = Configuration::ParseGpuDevice(value);
68 if ((tn) && device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
69 throw std::invalid_argument("gpu_device cannot change after initialization; clear the "
70 "simulator first");
71 configuration.SetConfiguration(key, std::to_string(device));
72 return;
73 }
74 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
75 if (!svdGroup.empty())
76 {
77 const bool enabled = Configuration::ParseGpuSvdFlag(value);
78 const bool gesvd = svdGroup == key;
79 const char algorithm = std::string(key).back();
80 const auto apply = [algorithm, enabled](auto &backend) {
81 if (!backend)
82 return true;
83 if (algorithm == 'j')
84 return backend->SetGesvdJ(enabled);
85 if (algorithm == 'p')
86 return backend->SetGesvdP(enabled);
87 return backend->SetGesvdR(enabled);
88 };
89 const auto applyGesvd = [enabled](auto &backend) {
90 if (!enabled || !backend)
91 return true;
92 return backend->SetGesvdJ(false) && backend->SetGesvdP(false) && backend->SetGesvdR(false);
93 };
94 bool applied = true;
95 if (svdGroup == "tensor_network_use_gesvd")
96 applied = gesvd ? applyGesvd(tn) : apply(tn);
97 if (!applied)
98 throw std::runtime_error(std::string("GPU library cannot apply ") + key + "; an updated GPU plugin may be required");
99 configuration.SetConfiguration(key, value);
100 return;
101 }
102 if (std::string("method") == key)
103 {
104 ValidateMethod(value);
105 configuration.SetConfiguration(key, value);
106 return;
107 }
108 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
109 {
110 // Precision is immutable after native storage is allocated.
111 // Replayed settings take effect after Clear and Initialize.
112 const bool useDouble = Configuration::ParsePrecision(key, value);
113 if ((tn && tn->IsCreated()))
114 return;
115 if ((tn && !tn->SetDataType(useDouble)))
116 throw std::runtime_error("GPU precision configuration failed");
117 configuration.SetConfiguration(key, value);
118 return;
119 }
120 if (!configuration.WasApplied(key, value))
121 configuration.SetConfiguration(key, value);
122 if (std::string("seed") == key)
123 {
124 const uint64_t seed = std::stoull(value);
125 SeedAuxiliaryRng(seed);
126 nextSeedStream = 0;
127 if (tn)
128 tn->SetSeed(seed);
129 return;
130 }
131 if (std::string("matrix_product_state_truncation_threshold") == key || std::string("matrix_product_operator_truncation_threshold") == key)
132 {
133 const double singularValueThreshold = std::stod(value);
134 if (singularValueThreshold >= 0.)
135 {
136 if (tn)
137 tn->SetCutoff(singularValueThreshold);
138 }
139 }
140 else if (std::string("matrix_product_state_truncation_mode") == key || std::string("matrix_product_operator_truncation_mode") == key)
141 {
142 int truncationMode = -1;
143 if (std::string("relative_max") == value)
144 truncationMode = 0;
145 else if (std::string("discarded_weight") == value)
146 truncationMode = 1;
147 if (truncationMode >= 0)
148 {
149 if (tn)
150 tn->SetTruncationMode(truncationMode);
151 }
152 }
153 else if (std::string("matrix_product_state_max_bond_dimension") == key || std::string("matrix_product_operator_max_bond_dimension") == key)
154 {
155 const long long int chi = std::stoi(value);
156 if (chi > 0)
157 {
158 if (tn)
159 tn->SetMaxExtent(chi);
160 }
161 }
162 }
163
164 std::string GetConfiguration(const char *key) const override
165 {
166 if (!key)
167 return {};
168 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
169 {
170 const bool useDouble = tn ? tn->IsDoublePrecision() : configuration.GetConfiguration("precision") == "double";
171 return std::string(key) == "precision" ? (useDouble ? "double" : "single") : (useDouble ? "1" : "0");
172 }
173 if (!key)
174 return {};
175 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
176 if (!svdGroup.empty())
177 {
178 if (svdGroup == key)
179 {
180 const auto readGesvd = [](const auto &backend) { return !backend->GetGesvdJ() && !backend->GetGesvdP() && !backend->GetGesvdR(); };
181 if (svdGroup == "tensor_network_use_gesvd" && tn)
182 return readGesvd(tn) ? "true" : "false";
183 }
184 const char algorithm = std::string(key).back();
185 const auto read = [algorithm](const auto &backend) {
186 if (algorithm == 'j')
187 return backend->GetGesvdJ();
188 if (algorithm == 'p')
189 return backend->GetGesvdP();
190 return backend->GetGesvdR();
191 };
192 if (svdGroup == "tensor_network_use_gesvd" && tn)
193 return read(tn) ? "true" : "false";
194 }
195 if (std::string("method") == key)
196 return MethodName();
197 return configuration.GetConfiguration(key);
198 }
199
200 size_t AllocateQubits(size_t num_qubits) override
201 {
202 const size_t oldNrQubits = nrQubits;
203 nrQubits += num_qubits;
204 return oldNrQubits;
205 }
206
207 void Clear() override
208 {
209 tn = nullptr;
210 nrQubits = 0;
211 upcomingGateIndex = 0;
212 upcomingGates.clear();
213 }
214
215 size_t Measure(const Types::qubits_vector &qubits) override
216 {
217 if (qubits.size() > sizeof(size_t) * 8)
218 std::cerr << "Warning: The number of qubits to measure is larger than the "
219 "number of bits in the size_t type, the outcome will be undefined"
220 << std::endl;
221 size_t res = 0;
222 size_t mask = 1ULL;
223 DontNotify();
224 {
225 for (size_t qubit : qubits)
226 {
227 if (tn->Measure(static_cast<unsigned int>(qubit)))
228 res |= mask;
229 mask <<= 1;
230 }
231 }
232 Notify();
233 NotifyObservers(qubits);
234 return res;
235 }
236
237 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override
238 {
239 std::vector<bool> res(qubits.size(), false);
240 DontNotify();
241 {
242 for (size_t i = 0; i < qubits.size(); ++i)
243 res[i] = tn->Measure(static_cast<unsigned int>(qubits[i]));
244 }
245 Notify();
246 NotifyObservers(qubits);
247 return res;
248 }
249
250 void ApplyReset(const Types::qubits_vector &qubits) override
251 {
252 DontNotify();
253 {
254 for (size_t qubit : qubits)
255 if (tn->Measure(static_cast<unsigned int>(qubit)))
256 tn->ApplyX(static_cast<unsigned int>(qubit));
257 }
258 Notify();
259 NotifyObservers(qubits);
260 }
261
262 std::complex<double> Amplitude(Types::qubit_t outcome) override
263 {
264 double real = 0.0;
265 double imag = 0.0;
266 {
267 std::vector<long int> fixedValues(nrQubits);
268 for (size_t i = 0; i < nrQubits; ++i)
269 fixedValues[i] = i < std::numeric_limits<Types::qubit_t>::digits ? (outcome >> i) & 1 : 0;
270 tn->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
271 }
272 return std::complex<double>(real, imag);
273 }
274
275 std::vector<double> AllProbabilities() override
276 {
277 if (nrQubits == 0)
278 return {};
279 if (nrQubits >= std::numeric_limits<size_t>::digits)
280 throw std::length_error("Full probability enumeration exceeds the basis-index API; query "
281 "selected bit vectors");
282 const size_t numStates = size_t{1} << nrQubits;
283 std::vector<double> result(numStates);
284 {
285 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i)
286 {
287 const auto val = Amplitude(i);
288 result[i] = std::norm(std::complex<double>(val.real(), val.imag()));
289 }
290 }
291 return result;
292 }
293
294 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(const Types::qubits_vector &qubits, size_t shots = 1000) override
295 {
296 if (qubits.empty() || shots == 0)
297 return {};
298 if (qubits.size() > sizeof(Types::qubit_t) * 8)
299 std::cerr << "Warning: The number of qubits to measure is larger than the "
300 "number of bits in the Types::qubit_t type, the outcome will be "
301 "undefined"
302 << std::endl;
303 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
304 DontNotify();
305 {
306 std::unordered_map<std::vector<bool>, int64_t> *map = tn->GetMapForSample();
307 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
308 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
309 const auto positions = SampleBitPositions(qubits);
310 for (const auto &[meas, cnt] : *map)
311 {
312 Types::qubit_t outcome = 0;
313 Types::qubit_t mask = 1ULL;
314 for (Types::qubit_t q = 0; q < qubits.size(); ++q)
315 {
316 if (meas[positions[q]])
317 outcome |= mask;
318 mask <<= 1;
319 }
320 result[outcome] += cnt;
321 }
322 tn->FreeMapForSample(map);
323 }
324 Notify();
325 NotifyObservers(qubits);
326 return result;
327 }
328
329 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(const Types::qubits_vector &qubits, size_t shots = 1000) override
330 {
331 if (qubits.empty() || shots == 0)
332 return {};
333 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
334 DontNotify();
335 {
336 std::unordered_map<std::vector<bool>, int64_t> *map = tn->GetMapForSample();
337 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
338 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
339 const auto positions = SampleBitPositions(qubits);
340 for (const auto &[meas, cnt] : *map)
341 {
342 std::vector<bool> ordered(qubits.size());
343 for (size_t q = 0; q < qubits.size(); ++q)
344 ordered[q] = meas[positions[q]];
345 result[ordered] += cnt;
346 }
347 tn->FreeMapForSample(map);
348 }
349 Notify();
350 NotifyObservers(qubits);
351 return result;
352 }
353
354 double ExpectationValue(const std::string &pauliString) override
355 {
356 double result = 0.0;
357 result = tn->ExpectationValue(pauliString);
358 return result;
359 }
360
361 int GetGpuDevice() const override
362 {
363 if (tn)
364 return tn->GetGpuDevice();
365 return -1;
366 }
367
368 SimulationType GetSimulationType() const override
369 {
370 return SimulationType::kTensorNetwork;
371 }
372
373 void SaveState() override
374 {
375 tn->SaveState();
376 }
377
378 void RestoreState() override
379 {
380 tn->RestoreState();
381 }
382
383 void ApplyP(Types::qubit_t qubit, double lambda) override
384 {
385 tn->ApplyP(qubit, lambda);
386 NotifyObservers({qubit});
387 }
388
389 void ApplyX(Types::qubit_t qubit) override
390 {
391 tn->ApplyX(qubit);
392 NotifyObservers({qubit});
393 }
394
395 void ApplyY(Types::qubit_t qubit) override
396 {
397 tn->ApplyY(qubit);
398 NotifyObservers({qubit});
399 }
400
401 void ApplyZ(Types::qubit_t qubit) override
402 {
403 tn->ApplyZ(qubit);
404 NotifyObservers({qubit});
405 }
406
407 void ApplyH(Types::qubit_t qubit) override
408 {
409 tn->ApplyH(qubit);
410 NotifyObservers({qubit});
411 }
412
413 void ApplyS(Types::qubit_t qubit) override
414 {
415 tn->ApplyS(qubit);
416 NotifyObservers({qubit});
417 }
418
419 void ApplySDG(Types::qubit_t qubit) override
420 {
421 tn->ApplySDG(qubit);
422 NotifyObservers({qubit});
423 }
424
425 void ApplyT(Types::qubit_t qubit) override
426 {
427 tn->ApplyT(qubit);
428 NotifyObservers({qubit});
429 }
430
431 void ApplyTDG(Types::qubit_t qubit) override
432 {
433 tn->ApplyTDG(qubit);
434 NotifyObservers({qubit});
435 }
436
437 void ApplySx(Types::qubit_t qubit) override
438 {
439 tn->ApplySX(qubit);
440 NotifyObservers({qubit});
441 }
442
443 void ApplySxDAG(Types::qubit_t qubit) override
444 {
445 tn->ApplySXDG(qubit);
446 NotifyObservers({qubit});
447 }
448
449 void ApplyK(Types::qubit_t qubit) override
450 {
451 tn->ApplyK(qubit);
452 NotifyObservers({qubit});
453 }
454
455 void ApplyRx(Types::qubit_t qubit, double theta) override
456 {
457 tn->ApplyRx(qubit, theta);
458 NotifyObservers({qubit});
459 }
460
461 void ApplyRy(Types::qubit_t qubit, double theta) override
462 {
463 tn->ApplyRy(qubit, theta);
464 NotifyObservers({qubit});
465 }
466
467 void ApplyRz(Types::qubit_t qubit, double theta) override
468 {
469 tn->ApplyRz(qubit, theta);
470 NotifyObservers({qubit});
471 }
472
473 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda, double gamma) override
474 {
475 tn->ApplyU(qubit, theta, phi, lambda, gamma);
476 NotifyObservers({qubit});
477 }
478
479 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
480 {
481 tn->ApplyCX(ctrl_qubit, tgt_qubit);
482 NotifyObservers({tgt_qubit, ctrl_qubit});
483 }
484
485 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
486 {
487 tn->ApplyCY(ctrl_qubit, tgt_qubit);
488 NotifyObservers({tgt_qubit, ctrl_qubit});
489 }
490
491 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
492 {
493 tn->ApplyCZ(ctrl_qubit, tgt_qubit);
494 NotifyObservers({tgt_qubit, ctrl_qubit});
495 }
496
497 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double lambda) override
498 {
499 tn->ApplyCP(ctrl_qubit, tgt_qubit, lambda);
500 NotifyObservers({tgt_qubit, ctrl_qubit});
501 }
502
503 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
504 {
505 tn->ApplyCRx(ctrl_qubit, tgt_qubit, theta);
506 NotifyObservers({tgt_qubit, ctrl_qubit});
507 }
508
509 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
510 {
511 tn->ApplyCRy(ctrl_qubit, tgt_qubit, theta);
512 NotifyObservers({tgt_qubit, ctrl_qubit});
513 }
514
515 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
516 {
517 tn->ApplyCRz(ctrl_qubit, tgt_qubit, theta);
518 NotifyObservers({tgt_qubit, ctrl_qubit});
519 }
520
521 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
522 {
523 tn->ApplyCH(ctrl_qubit, tgt_qubit);
524 NotifyObservers({tgt_qubit, ctrl_qubit});
525 }
526
527 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
528 {
529 tn->ApplyCSX(ctrl_qubit, tgt_qubit);
530 NotifyObservers({tgt_qubit, ctrl_qubit});
531 }
532
533 void ApplyCSxDAG(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
534 {
535 tn->ApplyCSXDG(ctrl_qubit, tgt_qubit);
536 NotifyObservers({tgt_qubit, ctrl_qubit});
537 }
538
539 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override
540 {
541 tn->ApplySwap(qubit0, qubit1);
542 NotifyObservers({qubit1, qubit0});
543 }
544
545 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1, Types::qubit_t qubit2) override
546 {
547 tn->ApplyCCX(qubit0, qubit1, qubit2);
548 NotifyObservers({qubit0, qubit1, qubit2});
549 }
550
551 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0, Types::qubit_t qubit1) override
552 {
553 tn->ApplyCSwap(ctrl_qubit, qubit0, qubit1);
554 NotifyObservers({qubit1, qubit0, ctrl_qubit});
555 }
556
557 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta, double phi, double lambda, double gamma) override
558 {
559 tn->ApplyCU(ctrl_qubit, tgt_qubit, theta, phi, lambda, gamma);
560 NotifyObservers({tgt_qubit, ctrl_qubit});
561 }
562
563 std::unique_ptr<ISimulator> Clone() override
564 {
565 throw std::runtime_error("GpuSimulator::Clone: Cloning Tensor Network or Pauli Propagator "
566 "simulation is not "
567 "supported.");
568 }
569
570 protected:
571 std::unique_ptr<GpuLibTNSim> tn;
572};
573} // namespace Simulators::Private
574#endif
int ApplyK(void *sim, int qubit)
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)
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 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 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