Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuLibMPSSim.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _GPU_LIB_MPS_SIM_H_
16#define _GPU_LIB_MPS_SIM_H_
17
18#ifdef __linux__
19
20#include <memory>
21#include <vector>
22
24#include "GpuDeviceContext.h"
25
26namespace Simulators
27{
28
29class GpuLibMPSSim
30{
31 public:
32 explicit GpuLibMPSSim(const std::shared_ptr<GpuLibrary> &lib, int device = -1) : lib(lib), obj(nullptr)
33 {
34 if (lib)
35 {
36 auto lock = lib->LockInitialization();
37 if (lib->SetGpuDevice(device == -1 ? lib->GetCreationDevice() : device))
38 obj = lib->CreateMPS();
39 }
40 }
41
42 int GetGpuDevice() const
43 {
44 return lib ? lib->MPSGetGpuId(obj) : -1;
45 }
46
47 GpuLibMPSSim(const std::shared_ptr<GpuLibrary> &lib, void *obj) : lib(lib), obj(obj)
48 {
49 }
50
51 GpuLibMPSSim() = delete;
52 GpuLibMPSSim(const GpuLibMPSSim &) = delete;
53 GpuLibMPSSim &operator=(const GpuLibMPSSim &) = delete;
54 GpuLibMPSSim(GpuLibMPSSim &&) = default;
55 GpuLibMPSSim &operator=(GpuLibMPSSim &&) = default;
56
57 ~GpuLibMPSSim()
58 {
59 if (lib && obj)
60 lib->DestroyMPS(obj);
61 }
62
63 bool Create(unsigned int nrQubits)
64 {
65 if (obj)
66 return lib->MPSCreate(obj, nrQubits);
67
68 return false;
69 }
70
71 bool CreateWithBasisState(unsigned int nrQubits, unsigned long long state)
72 {
73 if (obj)
74 return lib->MPSCreateWithBasisState(obj, nrQubits, state);
75
76 return false;
77 }
78
79 bool CreateWithBasisStateBits(unsigned int nrQubits, const std::vector<unsigned char> &stateBits)
80 {
81 if (obj)
82 return lib->MPSCreateWithBasisStateBits(obj, nrQubits, stateBits.data());
83
84 return false;
85 }
86
87 bool Reset()
88 {
89 if (obj)
90 return lib->MPSReset(obj);
91
92 return false;
93 }
94
95 bool SetSeed(uint64_t seed)
96 {
97 return obj && lib->MPSSetSeed(obj, seed);
98 }
99
100 // Explicit compression with the current cutoff, truncation mode and maximum
101 // extent: Trim re-splits over-cap bonds, ReCanonicalize repairs the chain.
102 bool Trim()
103 {
104 return obj && lib->MPSTrim(obj);
105 }
106
107 bool ReCanonicalize()
108 {
109 return obj && lib->MPSReCanonicalize(obj);
110 }
111
112 bool SetInitialQubitsMap(const std::vector<long long int> &initialMap)
113 {
114 if (obj)
115 return lib->MPSSetInitialQubitsMap(obj, initialMap);
116
117 return false;
118 }
119
120 bool SetUseOptimalMeetingPosition(bool useOptimalMeetingPosition)
121 {
122 if (obj)
123 return lib->MPSSetUseOptimalMeetingPosition(obj, useOptimalMeetingPosition);
124 return false;
125 }
126
127 bool GetUseOptimalMeetingPosition() const
128 {
129 if (obj)
130 return lib->MPSGetUseOptimalMeetingPosition(obj);
131 return false;
132 }
133
134 bool IsValid() const
135 {
136 if (obj)
137 return lib->MPSIsValid(obj);
138
139 return false;
140 }
141
142 bool IsCreated() const
143 {
144 if (obj)
145 return lib->MPSIsCreated(obj);
146
147 return false;
148 }
149
150 bool SetDataType(int useDoublePrecision)
151 {
152 if (obj)
153 return lib->MPSSetDataType(obj, useDoublePrecision);
154
155 return false;
156 }
157
158 bool IsDoublePrecision() const
159 {
160 if (obj)
161 return lib->MPSIsDoublePrecision(obj);
162
163 return false;
164 }
165
166 bool SetCutoff(double val)
167 {
168 if (obj)
169 return lib->MPSSetCutoff(obj, val);
170
171 return false;
172 }
173
174 double GetCutoff() const
175 {
176 if (obj)
177 return lib->MPSGetCutoff(obj);
178
179 return 0.;
180 }
181
182 // mode: 0 = RelativeToMax, 1 = DiscardedWeight (default). See
183 // TruncationMode in the GPU library's lib/truncationmode.hpp.
184 bool SetTruncationMode(int mode)
185 {
186 if (obj)
187 return lib->MPSSetTruncationMode(obj, mode);
188
189 return false;
190 }
191
192 int GetTruncationMode() const
193 {
194 if (obj)
195 return lib->MPSGetTruncationMode(obj);
196
197 return 0;
198 }
199
200 bool SetGesvdJ(int val)
201 {
202 if (obj)
203 return lib->MPSSetGesvdJ(obj, val);
204
205 return false;
206 }
207
208 bool GetGesvdJ() const
209 {
210 if (obj)
211 return lib->MPSGetGesvdJ(obj);
212
213 return false;
214 }
215
216 // Enabling any of J/P/R clears the other two selectors in the plugin.
217 bool SetGesvdP(bool enable)
218 {
219 return obj && lib->MPSSetGesvdP(obj, enable);
220 }
221
222 bool GetGesvdP() const
223 {
224 return lib->MPSGetGesvdP(obj);
225 }
226
227 bool SetGesvdR(bool enable)
228 {
229 return obj && lib->MPSSetGesvdR(obj, enable);
230 }
231
232 bool GetGesvdR() const
233 {
234 return lib->MPSGetGesvdR(obj);
235 }
236
237 int GetLastSvdAlgo() const
238 {
239 return lib->MPSGetLastSvdAlgo(obj);
240 }
241
242 bool SetMaxExtent(long int val)
243 {
244 if (obj)
245 return lib->MPSSetMaxExtent(obj, val);
246
247 return false;
248 }
249
250 long int GetMaxExtent() const
251 {
252 if (obj)
253 return lib->MPSGetMaxExtent(obj);
254
255 return 0;
256 }
257
258 int GetNrQubits() const
259 {
260 if (obj)
261 return lib->MPSGetNrQubits(obj);
262
263 return 0;
264 }
265
266 std::vector<long long int> GetBondDimensions(size_t nrQubits) const
267 {
268 if (!obj || nrQubits < 2)
269 return {};
270 std::vector<long long int> bondDims(nrQubits - 1);
271 if (!lib->MPSGetBondDimensions(obj, bondDims.data()))
272 return {};
273 return bondDims;
274 }
275
276 std::vector<long long> GetQubitsMap() const
277 {
278 std::vector<long long> map(GetNrQubits());
279 if (!lib->MPSGetQubitsMap(obj, map.data(), static_cast<int>(map.size())))
280 return {};
281 return map;
282 }
283
284 bool SetCallbackContext(void *context)
285 {
286 if (obj)
287 return lib->MPSSetCallbackContext(obj, context);
288 return false;
289 }
290
291 bool SetMeetingPositionCallback(int64_t (*callback)(void *, const int64_t *))
292 {
293 if (obj)
294 return lib->MPSSetMeetingPositionCallback(obj, callback);
295 return false;
296 }
297
298 bool SetBondDimensionsCallback(void (*callback)(void *, const int64_t *))
299 {
300 if (obj)
301 return lib->MPSSetBondDimensionsCallback(obj, callback);
302 return false;
303 }
304
305 void InstallBondSummary(void (*summary)(void *, int64_t), void (*full)(void *, const int64_t *))
306 {
307 const bool ok = lib->HasMPSBondSummary() ? lib->MPSSetBondDimensionSummaryCallback(obj, summary) : SetBondDimensionsCallback(full);
308 if (!ok)
309 throw std::runtime_error("GPU MPS bond callback installation failed");
310 }
311
312 std::vector<std::complex<double>> GetStateVector()
313 {
314 const auto n = GetNrQubits();
315 if (n < 0 || n > 40)
316 throw std::length_error("GPU MPS dense output supports at most 40 qubits");
317 const auto count = TensorQueries::DenseSize(n);
318 std::vector<std::complex<double>> result(count);
319 if (lib->HasMPSGetStateVector())
320 {
321 // std::complex<double> arrays have the interleaved layout required by
322 // the C API. The result is private until the native call succeeds.
323 if (!lib->MPSGetStateVector(obj, reinterpret_cast<double *>(result.data())))
324 throw std::runtime_error("GPU MPS statevector query failed");
325 }
326 else
327 {
328 std::vector<long int> bits(n);
329 for (size_t i = 0; i < count; ++i)
330 {
331 for (int q = 0; q < n; ++q)
332 bits[q] = (i >> q) & 1;
333 double re = 0., im = 0.;
334 if (!Amplitude(n, bits.data(), &re, &im) || !std::isfinite(re) || !std::isfinite(im))
335 throw std::runtime_error("GPU MPS amplitude query failed");
336 result[i] = {re, im};
337 }
338 }
339 return result;
340 }
341
342 std::vector<double> AllProbabilities()
343 {
344 const auto n = GetNrQubits();
345 if (n < 0 || n > 40)
346 throw std::length_error("GPU MPS dense output supports at most 40 qubits");
347 std::vector<double> result(TensorQueries::DenseSize(n));
348 if (lib->HasMPSAllProbabilities())
349 {
350 if (!lib->MPSAllProbabilities(obj, result.data()))
351 throw std::runtime_error("GPU MPS probability enumeration failed");
352 }
353 else
354 {
355 const auto amplitudes = GetStateVector();
356 for (size_t i = 0; i < result.size(); ++i)
357 result[i] = std::norm(amplitudes[i]);
358 }
359 return result;
360 }
361
362 void MoveAtBeginningOfChain(const std::vector<int> &qubits)
363 {
364 if (!lib->HasMPSMoveAtBeginningOfChain())
365 throw std::runtime_error("GPU MPS chain movement requires an updated GPU plugin");
366 if (!lib->MPSMoveAtBeginningOfChain(obj, qubits.data(), TensorQueries::Count(qubits.size())))
367 throw std::runtime_error("GPU MPS chain movement failed");
368 }
369
370 std::complex<double> ExpectationValueOperators(const std::vector<int> &qubits, const std::vector<double> &matrices)
371 {
372 if (!lib->HasMPSExpectationValueOperators())
373 throw std::runtime_error("GPU MPS operator expectations require an updated GPU plugin");
374 double re = 0., im = 0.;
375 if (!lib->MPSExpectationValueOperators(obj, TensorQueries::Count(qubits.size()), qubits.data(), matrices.data(), &re, &im))
376 throw std::runtime_error("GPU MPS operator expectation failed");
377 return {re, im};
378 }
379
380 std::vector<double> ExpectationValues(const std::vector<std::string> &paulis)
381 {
382 TensorQueries::PauliBatch batch(paulis);
383 std::vector<double> values(paulis.size());
384 if (paulis.empty())
385 return values;
386 if (lib->HasMPSExpectationValues())
387 {
388 if (!lib->MPSExpectationValues(obj, TensorQueries::Count(paulis.size()), batch.strings.data(), batch.lengths.data(), values.data()))
389 throw std::runtime_error("GPU MPS batch expectation failed");
390 }
391 else
392 {
393 for (size_t i = 0; i < paulis.size(); ++i)
394 {
395 values[i] = ExpectationValue(paulis[i]);
396 if (!std::isfinite(values[i]))
397 throw std::runtime_error("GPU MPS expectation failed");
398 }
399 }
400 return values;
401 }
402
403 std::unordered_map<std::vector<bool>, int64_t> SampleHistogram(size_t shots, std::vector<unsigned int> qubits)
404 {
405 if (shots == 0 || qubits.empty())
406 return {};
407 if (shots > static_cast<size_t>(std::numeric_limits<long int>::max()) || qubits.size() > static_cast<size_t>(std::numeric_limits<long int>::max()))
408 throw std::length_error("GPU MPS sampling size exceeds API limits");
409 for (auto q : qubits)
410 if (q >= static_cast<size_t>(GetNrQubits()))
411 throw std::out_of_range("GPU MPS sampled qubit is out of range");
412 using Map = std::unordered_map<std::vector<bool>, int64_t>;
413 auto deleter = [this](Map *map) { FreeMapForSample(map); };
414 std::unique_ptr<Map, decltype(deleter)> map(GetMapForSample(), deleter);
415 if (!map || !Sample(shots, qubits.size(), qubits.data(), map.get()))
416 throw std::runtime_error("GPU MPS sampling failed");
417 size_t total = 0;
418 for (const auto &entry : *map)
419 {
420 if (entry.second < 0 || static_cast<size_t>(entry.second) > shots - total)
421 throw std::runtime_error("GPU MPS returned invalid sample counts");
422 total += static_cast<size_t>(entry.second);
423 }
424 if (total != shots)
425 throw std::runtime_error("GPU MPS returned incomplete sample counts");
426 return *map;
427 }
428
429 bool Amplitude(long int numFixedValues, long int *fixedValues, double *real, double *imaginary) const
430 {
431 if (obj)
432 return lib->MPSAmplitude(obj, numFixedValues, fixedValues, real, imaginary);
433
434 return false;
435 }
436
437 double Probability0(unsigned int qubit) const
438 {
439 if (obj)
440 return lib->MPSProbability0(obj, qubit);
441
442 return 0.;
443 }
444
445 bool Measure(unsigned int qubit)
446 {
447 if (obj)
448 return lib->MPSMeasure(obj, qubit);
449
450 return 0;
451 }
452
453 bool MeasureQubits(long int numQubits, unsigned int *qubits, int *result)
454 {
455 if (obj)
456 return lib->MPSMeasureQubits(obj, numQubits, qubits, result);
457
458 return false;
459 }
460
461 std::unordered_map<std::vector<bool>, int64_t> *GetMapForSample() const
462 {
463 if (lib)
464 return lib->MPSGetMapForSample();
465
466 return nullptr;
467 }
468
469 bool FreeMapForSample(std::unordered_map<std::vector<bool>, int64_t> *map) const
470 {
471 if (lib)
472 return lib->MPSFreeMapForSample(map);
473
474 return false;
475 }
476
477 bool Sample(long int numShots, long int numQubits, unsigned int *qubits, void *resultMap)
478 {
479 if (obj)
480 return lib->MPSSample(obj, numShots, numQubits, qubits, resultMap);
481
482 return false;
483 }
484
485 bool SampleRaw(unsigned int nSamples, long int *samples, unsigned int nBits, const unsigned int *bitOrdering)
486 {
487 if (obj)
488 return lib->MPSSampleRaw(obj, nSamples, samples, nBits, bitOrdering);
489
490 return false;
491 }
492
493 bool SampleAll(unsigned int nSamples, long int *samples)
494 {
495 if (obj)
496 return lib->MPSSampleAll(obj, nSamples, samples);
497
498 return false;
499 }
500
501 bool SaveState()
502 {
503 if (obj)
504 return lib->MPSSaveState(obj);
505
506 return false;
507 }
508
509 bool RestoreState()
510 {
511 if (obj)
512 return lib->MPSRestoreState(obj);
513
514 return false;
515 }
516
517 bool CleanSavedState()
518 {
519 if (obj)
520 return lib->MPSCleanSavedState(obj);
521
522 return false;
523 }
524
525 std::unique_ptr<GpuLibMPSSim> Clone() const
526 {
527 if (obj)
528 {
529 auto *cloned = lib->MPSClone(obj);
530 if (!cloned)
531 throw std::runtime_error("GPU MPS clone failed");
532 return std::make_unique<GpuLibMPSSim>(lib, cloned);
533 }
534
535 return nullptr;
536 }
537
538 double ExpectationValue(const std::string &pauliString)
539 {
540 if (obj)
541 return lib->MPSExpectationValue(obj, pauliString.c_str(), pauliString.length());
542
543 return 0.0;
544 }
545
546 std::complex<double> ProjectOnZero()
547 {
548 if (obj)
549 return lib->MPSProjectOnZero(obj);
550
551 return std::complex<double>(0., 0.);
552 }
553
554 bool ApplyX(unsigned int siteA)
555 {
556 if (obj)
557 return lib->MPSApplyX(obj, siteA);
558
559 return false;
560 }
561
562 bool ApplyY(unsigned int siteA)
563 {
564 if (obj)
565 return lib->MPSApplyY(obj, siteA);
566
567 return false;
568 }
569
570 bool ApplyZ(unsigned int siteA)
571 {
572 if (obj)
573 return lib->MPSApplyZ(obj, siteA);
574
575 return false;
576 }
577
578 bool ApplyH(unsigned int siteA)
579 {
580 if (obj)
581 return lib->MPSApplyH(obj, siteA);
582
583 return false;
584 }
585
586 bool ApplyS(unsigned int siteA)
587 {
588 if (obj)
589 return lib->MPSApplyS(obj, siteA);
590
591 return false;
592 }
593
594 bool ApplySDG(unsigned int siteA)
595 {
596 if (obj)
597 return lib->MPSApplySDG(obj, siteA);
598
599 return false;
600 }
601
602 bool ApplyT(unsigned int siteA)
603 {
604 if (obj)
605 return lib->MPSApplyT(obj, siteA);
606
607 return false;
608 }
609
610 bool ApplyTDG(unsigned int siteA)
611 {
612 if (obj)
613 return lib->MPSApplyTDG(obj, siteA);
614
615 return false;
616 }
617
618 bool ApplySX(unsigned int siteA)
619 {
620 if (obj)
621 return lib->MPSApplySX(obj, siteA);
622
623 return false;
624 }
625
626 bool ApplySXDG(unsigned int siteA)
627 {
628 if (obj)
629 return lib->MPSApplySXDG(obj, siteA);
630
631 return false;
632 }
633
634 bool ApplyK(unsigned int siteA)
635 {
636 if (obj)
637 return lib->MPSApplyK(obj, siteA);
638
639 return false;
640 }
641
642 bool ApplyP(unsigned int siteA, double theta)
643 {
644 if (obj)
645 return lib->MPSApplyP(obj, siteA, theta);
646
647 return false;
648 }
649
650 bool ApplyRx(unsigned int siteA, double theta)
651 {
652 if (obj)
653 return lib->MPSApplyRx(obj, siteA, theta);
654
655 return false;
656 }
657
658 bool ApplyRy(unsigned int siteA, double theta)
659 {
660 if (obj)
661 return lib->MPSApplyRy(obj, siteA, theta);
662
663 return false;
664 }
665
666 bool ApplyRz(unsigned int siteA, double theta)
667 {
668 if (obj)
669 return lib->MPSApplyRz(obj, siteA, theta);
670
671 return false;
672 }
673
674 bool ApplyU(unsigned int siteA, double theta, double phi, double lambda, double gamma)
675 {
676 if (obj)
677 return lib->MPSApplyU(obj, siteA, theta, phi, lambda, gamma);
678
679 return false;
680 }
681
682 bool ApplyOneQubitMatrix(unsigned int qubit, const double *matrixInterleaved)
683 {
684 if (obj)
685 return lib->MPSApplyOneQubitMatrix(obj, qubit, matrixInterleaved);
686
687 return false;
688 }
689
690 bool ApplyTwoQubitMatrix(unsigned int qubit1, unsigned int qubit2, const double *matrixInterleaved)
691 {
692 if (obj)
693 return lib->MPSApplyTwoQubitMatrix(obj, qubit1, qubit2, matrixInterleaved);
694
695 return false;
696 }
697
698 bool ApplySwap(unsigned int controlQubit, unsigned int targetQubit)
699 {
700 if (obj)
701 return lib->MPSApplySwap(obj, controlQubit, targetQubit);
702
703 return false;
704 }
705
706 bool ApplyCX(unsigned int controlQubit, unsigned int targetQubit)
707 {
708 if (obj)
709 return lib->MPSApplyCX(obj, controlQubit, targetQubit);
710
711 return false;
712 }
713
714 bool ApplyCY(unsigned int controlQubit, unsigned int targetQubit)
715 {
716 if (obj)
717 return lib->MPSApplyCY(obj, controlQubit, targetQubit);
718
719 return false;
720 }
721
722 bool ApplyCZ(unsigned int controlQubit, unsigned int targetQubit)
723 {
724 if (obj)
725 return lib->MPSApplyCZ(obj, controlQubit, targetQubit);
726
727 return false;
728 }
729
730 bool ApplyCH(unsigned int controlQubit, unsigned int targetQubit)
731 {
732 if (obj)
733 return lib->MPSApplyCH(obj, controlQubit, targetQubit);
734
735 return false;
736 }
737
738 bool ApplyCSX(unsigned int controlQubit, unsigned int targetQubit)
739 {
740 if (obj)
741 return lib->MPSApplyCSX(obj, controlQubit, targetQubit);
742
743 return false;
744 }
745
746 bool ApplyCSXDG(unsigned int controlQubit, unsigned int targetQubit)
747 {
748 if (obj)
749 return lib->MPSApplyCSXDG(obj, controlQubit, targetQubit);
750
751 return false;
752 }
753
754 bool ApplyCP(unsigned int controlQubit, unsigned int targetQubit, double theta)
755 {
756 if (obj)
757 return lib->MPSApplyCP(obj, controlQubit, targetQubit, theta);
758
759 return false;
760 }
761
762 bool ApplyCRx(unsigned int controlQubit, unsigned int targetQubit, double theta)
763 {
764 if (obj)
765 return lib->MPSApplyCRx(obj, controlQubit, targetQubit, theta);
766
767 return false;
768 }
769
770 bool ApplyCRy(unsigned int controlQubit, unsigned int targetQubit, double theta)
771 {
772 if (obj)
773 return lib->MPSApplyCRy(obj, controlQubit, targetQubit, theta);
774
775 return false;
776 }
777
778 bool ApplyCRz(unsigned int controlQubit, unsigned int targetQubit, double theta)
779 {
780 if (obj)
781 return lib->MPSApplyCRz(obj, controlQubit, targetQubit, theta);
782
783 return false;
784 }
785
786 bool ApplyCU(unsigned int controlQubit, unsigned int targetQubit, double theta, double phi, double lambda, double gamma)
787 {
788 if (obj)
789 return lib->MPSApplyCU(obj, controlQubit, targetQubit, theta, phi, lambda, gamma);
790
791 return false;
792 }
793
794 private:
795 GpuDeviceContext lib;
796 void *obj;
797};
798
799} // namespace Simulators
800
801#endif // __linux__
802
803#endif // _GPU_LIB_MPS_SIM_H_
int ApplyK(void *sim, int qubit)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
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 ApplyCSXDG(void *sim, int controlQubit, int targetQubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
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 ApplySXDG(void *sim, int qubit)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
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 ApplySX(void *sim, int qubit)
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 ApplyCSX(void *sim, int controlQubit, int targetQubit)
int SaveState(void *sim)
Bounded online routing based on current physical bond dimensions.