Maestro 0.3.1
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
22#include "MPSDummySimulator.h"
23
24#include <cstdint>
25#include <iomanip>
26#include <limits>
27#include <sstream>
28#include <utility>
29
30#include "Configuration.h"
31
32namespace Simulators {
33// TODO: Maybe use the pimpl idiom
34// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
35// for good but during development this should be good enough
36namespace Private {
37
48class GpuState : public ISimulator {
49 public:
57 void Initialize() override {
58 if (nrQubits) {
59 auto initializationLock = GpuLibrary::GetInstance()->LockInitialization();
60 const int gpuDevice = configuration.IsSet("gpu_device")
61 ? Configuration::ParseGpuDevice(configuration.GetConfiguration("gpu_device"))
62 : SimulatorsFactory::ResolveGpuDevice();
63 configuration.SetConfiguration("gpu_device", std::to_string(gpuDevice));
64 if (!SimulatorsFactory::GetGpuLibrary(gpuDevice))
65 throw std::runtime_error("GpuState::Initialize: Unable to initialize GPU device " +
66 std::to_string(gpuDevice));
67 if (simulationType == SimulationType::kStatevector) {
68 state = SimulatorsFactory::CreateGpuLibStateVectorSim(gpuDevice);
69 if (state) {
70 // ensure the config settings are applied, they need to be applied
71 // after the simulator is created
72 for (const auto& [key, value] : configuration.GetConfigMap())
73 if (key != "method") Configure(key.c_str(), value.c_str());
74
75 const bool res = state->Create(nrQubits);
76 if (!res)
77 throw std::runtime_error(
78 "GpuState::Initialize: Failed to create "
79 "and initialize the statevector state.");
80 } else
81 throw std::runtime_error(
82 "GpuState::Initialize: Failed to create the statevector state.");
83 } else if (simulationType == SimulationType::kDensityMatrix) {
84 densityMatrix = SimulatorsFactory::CreateGpuDensityMatrix(gpuDevice);
85 if (!densityMatrix)
86 throw std::runtime_error(
87 "GpuState::Initialize: Failed to create the density matrix state.");
88 // Precision must be selected before native density-matrix storage is
89 // allocated.
90 for (const auto& [key, value] : configuration.GetConfigMap())
91 if (key != "method") Configure(key.c_str(), value.c_str());
92 if (!densityMatrix->Create(nrQubits))
93 throw std::runtime_error(
94 "GpuState::Initialize: Failed to initialize the density matrix state.");
95 } else if (simulationType == SimulationType::kMatrixProductOperator) {
96 mpo = SimulatorsFactory::CreateGpuMPO(gpuDevice);
97 if (!mpo)
98 throw std::runtime_error(
99 "GpuState::Initialize: Failed to create the matrix product "
100 "operator state.");
101 mpo->SetCallbackContext((void*)this);
102 curMaxBondDim = 1;
103 mpo->SetBondDimensionsCallback(&GpuState::BondDimCallback);
104
105 // Precision and truncation controls must be selected before native
106 // storage is allocated.
107 for (const auto& [key, value] : configuration.GetConfigMap())
108 if (key != "method") Configure(key.c_str(), value.c_str());
109 if (!mpo->Create(nrQubits))
110 throw std::runtime_error(
111 "GpuState::Initialize: Failed to initialize the matrix product "
112 "operator state.");
113 // default is true
114 if (!useOptimalMeetingPosition)
115 mpo->SetUseOptimalMeetingPosition(false);
116 } else if (simulationType == SimulationType::kMatrixProductState) {
117 mps = SimulatorsFactory::CreateGpuLibMPSSim(gpuDevice);
118 if (mps) {
119 mps->SetCallbackContext((void*)this);
120 curMaxBondDim = 1;
121 mps->SetBondDimensionsCallback(&GpuState::BondDimCallback);
122
123 // ensure the config settings are applied, they need to be applied
124 // after the simulator is created but before the state is created
125 for (const auto& [key, value] : configuration.GetConfigMap())
126 if (key != "method") Configure(key.c_str(), value.c_str());
127
128 const bool res = mps->Create(nrQubits);
129 if (!res)
130 throw std::runtime_error(
131 "GpuState::Initialize: Failed to create "
132 "and initialize the MPS state.");
133 } else
134 throw std::runtime_error(
135 "GpuState::Initialize: Failed to create the MPS state.");
136 // default is true
137 if (!useOptimalMeetingPosition)
138 mps->SetUseOptimalMeetingPosition(false);
139 } else if (simulationType == SimulationType::kTensorNetwork) {
140 tn = SimulatorsFactory::CreateGpuLibTensorNetSim(gpuDevice);
141 if (tn) {
142 // ensure the config settings are applied, they need to be applied
143 // after the simulator is created but before the state is created
144 for (const auto& [key, value] : configuration.GetConfigMap())
145 if (key != "method") Configure(key.c_str(), value.c_str());
146
147 const bool res = tn->Create(nrQubits);
148 if (!res)
149 throw std::runtime_error(
150 "GpuState::Initialize: Failed to create "
151 "and initialize the tensor network state.");
152 } else
153 throw std::runtime_error(
154 "GpuState::Initialize: Failed to create the tensor network "
155 "state.");
156 } else if (simulationType == SimulationType::kPauliPropagator) {
157 pp = SimulatorsFactory::CreateGpuPauliPropagatorSimulatorUnique(gpuDevice);
158 if (pp) {
159 // ensure the config settings are applied, they need to be applied
160 // after the simulator is created but before the state is created
161 for (const auto& [key, value] : configuration.GetConfigMap())
162 if (key != "method") Configure(key.c_str(), value.c_str());
163
164 const bool res = pp->CreateSimulator(nrQubits);
165 if (!res)
166 throw std::runtime_error(
167 "GpuState::Initialize: Failed to create "
168 "and initialize the Pauli propagator state.");
169
170 pp->SetWillUseSampling(true); // TODO: check setting
171 if (!pp->AllocateMemory(0.9))
172 throw std::runtime_error(
173 "GpuState::Initialize: Failed to allocate memory for the "
174 "Pauli propagator state.");
175 } else
176 throw std::runtime_error(
177 "GpuState::Initialize: Failed to create the Pauli propagator "
178 "state.");
179 } else
180 throw std::runtime_error(
181 "GpuState::Initialize: Invalid simulation "
182 "type for initializing the state.");
183 if (GetGpuDevice() != gpuDevice)
184 throw std::runtime_error("GpuState::Initialize: GPU plugin did not confirm the requested device; update the GPU library");
185 }
186 }
187
199 void InitializeState(size_t num_qubits,
200 std::vector<std::complex<double>> &amplitudes) override {
201 if (num_qubits == 0) return;
202 Clear();
203 nrQubits = num_qubits;
204 Initialize();
205
206 if (simulationType != SimulationType::kStatevector &&
207 simulationType != SimulationType::kDensityMatrix &&
208 simulationType != SimulationType::kMatrixProductOperator)
209 throw std::runtime_error(
210 "GpuState::InitializeState: Invalid simulation "
211 "type for initializing the state.");
212
213 const bool created =
214 simulationType == SimulationType::kDensityMatrix
215 ? densityMatrix->CreateWithState(
216 nrQubits,
217 reinterpret_cast<const double *>(amplitudes.data()))
218 : simulationType == SimulationType::kMatrixProductOperator
219 ? mpo->CreateWithState(
220 nrQubits,
221 reinterpret_cast<const double *>(amplitudes.data()))
222 : state->CreateWithState(
223 nrQubits,
224 reinterpret_cast<const double *>(amplitudes.data()));
225 if (!created)
226 throw std::runtime_error(
227 "GpuState::InitializeState: Failed to initialize the state.");
228 }
229
241#ifndef NO_QISKIT_AER
242 void InitializeState(size_t num_qubits,
243 AER::Vector<std::complex<double>> &amplitudes) override {
244 if (num_qubits == 0) return;
245 Clear();
246 nrQubits = num_qubits;
247 Initialize();
248
249 if (simulationType != SimulationType::kStatevector &&
250 simulationType != SimulationType::kDensityMatrix &&
251 simulationType != SimulationType::kMatrixProductOperator)
252 throw std::runtime_error(
253 "GpuState::InitializeState: Invalid simulation "
254 "type for initializing the state.");
255
256 const bool created =
257 simulationType == SimulationType::kDensityMatrix
258 ? densityMatrix->CreateWithState(
259 nrQubits,
260 reinterpret_cast<const double *>(amplitudes.data()))
261 : simulationType == SimulationType::kMatrixProductOperator
262 ? mpo->CreateWithState(
263 nrQubits,
264 reinterpret_cast<const double *>(amplitudes.data()))
265 : state->CreateWithState(
266 nrQubits,
267 reinterpret_cast<const double *>(amplitudes.data()));
268 if (!created)
269 throw std::runtime_error(
270 "GpuState::InitializeState: Failed to initialize the state.");
271 }
272#endif
273
285 void InitializeState(size_t num_qubits,
286 Eigen::VectorXcd &amplitudes) override {
287 if (num_qubits == 0) return;
288 Clear();
289 nrQubits = num_qubits;
290 Initialize();
291
292 if (simulationType != SimulationType::kStatevector &&
293 simulationType != SimulationType::kDensityMatrix &&
294 simulationType != SimulationType::kMatrixProductOperator)
295 throw std::runtime_error(
296 "GpuState::InitializeState: Invalid simulation "
297 "type for initializing the state.");
298
299 const bool created =
300 simulationType == SimulationType::kDensityMatrix
301 ? densityMatrix->CreateWithState(
302 nrQubits,
303 reinterpret_cast<const double *>(amplitudes.data()))
304 : simulationType == SimulationType::kMatrixProductOperator
305 ? mpo->CreateWithState(
306 nrQubits,
307 reinterpret_cast<const double *>(amplitudes.data()))
308 : state->CreateWithState(
309 nrQubits,
310 reinterpret_cast<const double *>(amplitudes.data()));
311 if (!created)
312 throw std::runtime_error(
313 "GpuState::InitializeState: Failed to initialize the state.");
314 }
315
327 void InitializeToBasisState(size_t num_qubits,
328 Types::qubit_t basisState) override {
329 if (num_qubits == 0) return;
330 Clear();
331 nrQubits = num_qubits;
332 Initialize();
333
334 bool created = true;
335 if (simulationType == SimulationType::kDensityMatrix)
336 created = densityMatrix->CreateWithBasisState(
337 nrQubits, static_cast<unsigned long long>(basisState));
338 else if (simulationType == SimulationType::kMatrixProductOperator)
339 created = mpo->CreateWithBasisState(
340 nrQubits, static_cast<unsigned long long>(basisState));
341 else if (simulationType == SimulationType::kMatrixProductState)
342 created = mps->CreateWithBasisState(
343 nrQubits, static_cast<unsigned long long>(basisState));
344 else
345 for (size_t q = 0; q < num_qubits; ++q)
346 if ((basisState >> q) & 1ULL) ApplyX(static_cast<Types::qubit_t>(q));
347
348 if (!created)
349 throw std::runtime_error(
350 "GpuState::InitializeToBasisState: Failed to initialize the "
351 "state.");
352 }
353
367 void InitializeToBasisState(size_t num_qubits,
368 const std::vector<bool> &basisState) override {
369 if (num_qubits == 0) return;
370 Clear();
371 nrQubits = num_qubits;
372 Initialize();
373
374 bool created = true;
375 if (simulationType == SimulationType::kMatrixProductOperator ||
376 simulationType == SimulationType::kMatrixProductState) {
377 std::vector<unsigned char> stateBits(num_qubits, 0);
378 for (size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
379 stateBits[q] = basisState[q] ? 1 : 0;
380 created = simulationType == SimulationType::kMatrixProductOperator
381 ? mpo->CreateWithBasisStateBits(nrQubits, stateBits)
382 : mps->CreateWithBasisStateBits(nrQubits, stateBits);
383 } else
384 for (size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
385 if (basisState[q]) ApplyX(static_cast<Types::qubit_t>(q));
386
387 if (!created)
388 throw std::runtime_error(
389 "GpuState::InitializeToBasisState: Failed to initialize the "
390 "state.");
391 }
392
403 void InitializeToMixtureOfBasisStates(
404 size_t num_qubits,
405 const std::vector<std::pair<Types::qubit_t, double>> &mixture)
406 override {
407 if (num_qubits == 0) return;
408 Clear();
409 nrQubits = num_qubits;
410 Initialize();
411
412 if (simulationType != SimulationType::kDensityMatrix &&
413 simulationType != SimulationType::kMatrixProductOperator)
414 throw std::runtime_error(
415 "GpuState::InitializeToMixtureOfBasisStates: Invalid simulation "
416 "type for initializing to a mixture of basis states.");
417
418 std::vector<std::pair<unsigned long long, double>> converted;
419 converted.reserve(mixture.size());
420 for (const auto &[basisState, weight] : mixture)
421 converted.emplace_back(static_cast<unsigned long long>(basisState),
422 weight);
423
424 const bool created =
425 simulationType == SimulationType::kDensityMatrix
426 ? densityMatrix->CreateWithMixtureOfBasisStates(nrQubits,
427 converted)
428 : mpo->CreateWithMixtureOfBasisStates(nrQubits, converted);
429
430 if (!created)
431 throw std::runtime_error(
432 "GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
433 "the state.");
434 }
435
446 void InitializeToMixtureOfBasisStates(
447 size_t num_qubits,
448 const std::vector<std::pair<std::vector<bool>, double>> &mixture)
449 override {
450 if (num_qubits == 0) return;
451 Clear();
452 nrQubits = num_qubits;
453 Initialize();
454
455 if (simulationType != SimulationType::kMatrixProductOperator)
456 throw std::runtime_error(
457 "GpuState::InitializeToMixtureOfBasisStates: Invalid simulation "
458 "type for initializing to a mixture of basis states.");
459
460 std::vector<double> weights;
461 weights.reserve(mixture.size());
462 std::vector<unsigned char> stateBitsFlat;
463 stateBitsFlat.reserve(mixture.size() * num_qubits);
464 for (const auto &[basisState, weight] : mixture) {
465 weights.push_back(weight);
466 for (size_t q = 0; q < num_qubits; ++q)
467 stateBitsFlat.push_back(
468 (q < basisState.size() && basisState[q]) ? 1 : 0);
469 }
470
471 if (!mpo->CreateWithMixtureOfBasisStatesBits(nrQubits, stateBitsFlat,
472 weights))
473 throw std::runtime_error(
474 "GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
475 "the state.");
476 }
477
484 void Reset() override {
485 if (state)
486 state->Reset();
487 else if (densityMatrix)
488 densityMatrix->Reset();
489 else if (mpo) {
490 mpo->Reset();
491 curMaxBondDim = 1;
492 } else if (mps) {
493 mps->Reset();
494 curMaxBondDim = 1;
495 } else if (tn)
496 tn->Reset();
497 else if (pp)
498 pp->ClearOperators();
499
500 upcomingGateIndex = 0;
501 }
502
510 bool SupportsMPSSwapOptimization() const override { return true; }
511
520 void SetInitialQubitsMap(
521 const std::vector<long long int> &initialMap) override {
522 if (mps || mpo) {
523 if (mps) mps->SetInitialQubitsMap(initialMap);
524 else mpo->SetInitialQubitsMap(initialMap);
525 if (!dummySim || dummySim->getNrQubits() != initialMap.size()) {
526 dummySim =
527 std::make_unique<Simulators::MPSDummySimulator>(initialMap.size());
528 dummySim->SetMaxBondDimension(
529 configuration.GetConfigurationAsInt(MaxBondDimensionConfigKey()));
530 }
531 dummySim->setGrowthFactorGate(growthFactorGate);
532 dummySim->setGrowthFactorSwap(growthFactorSwap);
533 dummySim->SetInitialQubitsMap(initialMap);
534 }
535 }
536
537 void SetUseOptimalMeetingPosition(bool enable) override {
538 useOptimalMeetingPosition = enable;
539 if (mps || mpo) {
540 if (mps) mps->SetUseOptimalMeetingPosition(enable);
541 else mpo->SetUseOptimalMeetingPosition(enable);
542
543 if (enable) {
544 // Register an observer that advances the gate index
545 ClearObservers(); // for now we only have this observer, so this should
546 // be fine
547 gateCounterObserver =
548 std::make_shared<GateCounterObserver>(upcomingGateIndex);
549 RegisterObserver(gateCounterObserver);
550
551 // Set up a meeting position callback that uses MPSDummySimulator
552 // for lookahead evaluation with actual bond dimensions
553 // the callback is called only for two qubits gates and only if
554 // executing them would require a swap
555 if (mps)
556 mps->SetMeetingPositionCallback(&GpuState::FindBestMeetingPosition);
557 else
558 mpo->SetMeetingPositionCallback(&GpuState::FindBestMeetingPosition);
559 }
560 }
561 }
562
563 void SetLookaheadDepth(int depth) override {
564 lookaheadDepth = depth;
565 if (depth > 0 && !useOptimalMeetingPosition) {
566 if (mps) mps->SetUseOptimalMeetingPosition(true);
567 else if (mpo) mpo->SetUseOptimalMeetingPosition(true);
568 }
569 }
570
571 void SetLookaheadDepthWithHeuristic(int depth) override {
572 lookaheadDepthWithHeuristic = depth;
573 if (lookaheadDepth < depth) SetLookaheadDepth(depth);
574 }
575
576 void SetUpcomingGates(
577 const std::vector<std::shared_ptr<Circuits::IOperation<double>>> &gates)
578 override {
579 upcomingGates = gates;
580 upcomingGateIndex = 0;
581
582 if (!mps && !mpo) return;
583
584 // Register an observer that advances the gate index
585 ClearObservers(); // for now we only have this observer, so this should be
586 // fine
587 gateCounterObserver =
588 std::make_shared<GateCounterObserver>(upcomingGateIndex);
589 RegisterObserver(gateCounterObserver);
590
591 // Set up a meeting position callback that uses MPSDummySimulator
592 // for lookahead evaluation with actual bond dimensions
593 // the callback is called only for two qubits gates and only if executing
594 // them would require a swap
595 if (mps)
596 mps->SetMeetingPositionCallback(&GpuState::FindBestMeetingPosition);
597 else
598 mpo->SetMeetingPositionCallback(&GpuState::FindBestMeetingPosition);
599 }
600
609 long long int GetGatesCounter() const override { return upcomingGateIndex; }
610
620 void SetGatesCounter(long long int counter) override {
621 upcomingGateIndex = counter;
622 }
623
632 void IncrementGatesCounter() override { ++upcomingGateIndex; }
633
634 double getGrowthFactorSwap() const override { return growthFactorSwap; }
635 double getGrowthFactorGate() const override { return growthFactorGate; }
636
637 void setGrowthFactorSwap(double factor) override {
638 growthFactorSwap = factor;
639 if (dummySim) dummySim->setGrowthFactorSwap(factor);
640 }
641
642 void setGrowthFactorGate(double factor) override {
643 growthFactorGate = factor;
644 if (dummySim) dummySim->setGrowthFactorGate(factor);
645 }
646
655 void Configure(const char *key, const char *value) override {
656 if (!key || !value) return;
657 if (std::string("gpu_device") == key) {
658 const int device = Configuration::ParseGpuDevice(value);
659 if ((state || densityMatrix || mpo || mps || tn || pp) &&
660 device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
661 throw std::invalid_argument("gpu_device cannot change after initialization; clear the simulator first");
662 configuration.SetConfiguration(key, std::to_string(device));
663 return;
664 }
665 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
666 if (!svdGroup.empty()) {
667 const bool enabled = Configuration::ParseGpuSvdFlag(value);
668 const bool gesvd = svdGroup == key;
669 const char algorithm = std::string(key).back();
670 const auto apply = [algorithm, enabled](auto& backend) {
671 if (!backend) return true; // Applied after native object creation.
672 if (algorithm == 'j') return backend->SetGesvdJ(enabled);
673 if (algorithm == 'p') return backend->SetGesvdP(enabled);
674 return backend->SetGesvdR(enabled);
675 };
676 const auto applyGesvd = [enabled](auto& backend) {
677 // GESVD has no native flag. A false entry is an inactive selector,
678 // so replaying it must not clear a subsequently selected algorithm.
679 if (!enabled || !backend) return true;
680 return backend->SetGesvdJ(false) && backend->SetGesvdP(false) &&
681 backend->SetGesvdR(false);
682 };
683 bool applied = true;
684 if (svdGroup == "matrix_product_state_use_gesvd")
685 applied = gesvd ? applyGesvd(mps) : apply(mps);
686 else if (svdGroup == "matrix_product_operator_use_gesvd")
687 applied = gesvd ? applyGesvd(mpo) : apply(mpo);
688 else if (svdGroup == "tensor_network_use_gesvd")
689 applied = gesvd ? applyGesvd(tn) : apply(tn);
690 if (!applied)
691 throw std::runtime_error(std::string("GPU library cannot apply ") + key +
692 "; an updated GPU plugin may be required");
693 configuration.SetConfiguration(key, value);
694 return;
695 }
696 if (std::string("method") == key) {
697 if (std::string("statevector") == value)
698 simulationType = SimulationType::kStatevector;
699 else if (std::string("matrix_product_state") == value)
700 simulationType = SimulationType::kMatrixProductState;
701 else if (std::string("density_matrix") == value)
702 simulationType = SimulationType::kDensityMatrix;
703 else if (std::string("matrix_product_operator") == value)
704 simulationType = SimulationType::kMatrixProductOperator;
705 else if (std::string("tensor_network") == value)
706 simulationType = SimulationType::kTensorNetwork;
707 else if (std::string("pauli_propagator") == value)
708 simulationType = SimulationType::kPauliPropagator;
709
710 // Match the GPU library's default cap.
711 if ((simulationType == SimulationType::kMatrixProductState ||
712 simulationType == SimulationType::kMatrixProductOperator) &&
713 !configuration.IsSet("matrix_product_state_max_bond_dimension"))
714 configuration.SetConfiguration(
715 "matrix_product_state_max_bond_dimension", "128");
716 if (simulationType == SimulationType::kMatrixProductOperator) {
717 const auto bondDimension =
718 configuration.GetConfiguration(MaxBondDimensionConfigKey());
719 configuration.SetConfiguration(
720 "matrix_product_state_max_bond_dimension", bondDimension);
721 configuration.SetConfiguration(
722 "matrix_product_operator_max_bond_dimension", bondDimension);
723 }
724 }
725
726 if (std::string("use_double_precision") == key && densityMatrix &&
727 densityMatrix->IsCreated())
728 throw std::runtime_error(
729 "GpuState::Configure: Density-matrix precision must be configured "
730 "before initialization.");
731
732 if (std::string("use_double_precision") == key && mpo && mpo->IsCreated())
733 throw std::runtime_error(
734 "GpuState::Configure: Matrix-product-operator precision must be "
735 "configured before initialization.");
736
737 if (!configuration.WasApplied(key, value))
738 configuration.SetConfiguration(key, value);
739
740 if (std::string("seed") == key) {
741 const uint64_t seed = std::stoull(value);
742 nextSeedStream = 0;
743 if (state) state->SetSeed(seed);
744 if (densityMatrix) densityMatrix->SetSeed(seed);
745 if (mpo) mpo->SetSeed(seed);
746 if (mps) mps->SetSeed(seed);
747 if (tn) tn->SetSeed(seed);
748 if (pp) pp->SetSeed(seed);
749 return;
750 }
751
752 if (std::string("matrix_product_state_truncation_threshold") == key ||
753 std::string("matrix_product_operator_truncation_threshold") == key) {
754 // SetCutoff() sets the numeric threshold value. How that number is interpreted --
755 // relative to the largest singular value at each bond/split, or as a cumulative
756 // discarded-weight budget -- is a separate, independently configurable setting; see
757 // matrix_product_state_truncation_mode / matrix_product_operator_truncation_mode
758 // below. All three GPU backends default to discarded-weight (matching Qiskit Aer's
759 // and ITensor's convention) unless relative_max is explicitly requested -- see
760 // TruncationMode in maestro-gpu-simulators' lib/truncationmode.hpp and its use in
761 // mpsimpl.cu/mpo.cu/tensornet.cu.
762 const double singularValueThreshold = std::stod(value);
763 if (singularValueThreshold > 0.) {
764 if (mps) mps->SetCutoff(singularValueThreshold);
765 if (tn) tn->SetCutoff(singularValueThreshold);
766 if (mpo) mpo->SetCutoff(singularValueThreshold);
767 }
768 } else if (std::string("matrix_product_state_truncation_mode") == key ||
769 std::string("matrix_product_operator_truncation_mode") == key) {
770 // "relative_max" -> TruncationMode::RelativeToMax (0), "discarded_weight" ->
771 // TruncationMode::DiscardedWeight (1, the default -- see lib/truncationmode.hpp in
772 // maestro-gpu-simulators).
773 int truncationMode = -1;
774 if (std::string("relative_max") == value)
775 truncationMode = 0;
776 else if (std::string("discarded_weight") == value)
777 truncationMode = 1;
778 if (truncationMode >= 0) {
779 if (mps) mps->SetTruncationMode(truncationMode);
780 if (tn) tn->SetTruncationMode(truncationMode);
781 if (mpo) mpo->SetTruncationMode(truncationMode);
782 }
783 } else if (std::string("matrix_product_state_max_bond_dimension") == key ||
784 std::string("matrix_product_operator_max_bond_dimension") ==
785 key) {
786 const long long int chi = std::stoi(value);
787 if (simulationType == SimulationType::kMatrixProductOperator) {
788 // Both names address the same MPO setting; the latest write wins.
789 const std::string bondDimension(value);
790 for (const char* alias : {"matrix_product_state_max_bond_dimension",
791 "matrix_product_operator_max_bond_dimension"})
792 if (!configuration.WasApplied(alias, bondDimension))
793 configuration.SetConfiguration(alias, bondDimension);
794 }
795 if (chi > 0) {
796 if (mps) mps->SetMaxExtent(chi);
797 if (tn) tn->SetMaxExtent(chi);
798 if (mpo) mpo->SetMaxExtent(chi);
799 if (dummySim) dummySim->SetMaxBondDimension(chi);
800 }
801
802 } else if (std::string("matrix_product_operator_kraus_completeness_check") == key) {
803 int mode = -1;
804 if (std::string(value) == "ignore") mode = 0;
805 else if (std::string(value) == "warn") mode = 1;
806 else if (std::string(value) == "strict") mode = 2;
807 if (mpo && mode >= 0 && !mpo->SetKrausCompletenessCheck(mode))
808 throw std::runtime_error("Invalid GPU MPO Kraus completeness mode");
809 } else if (std::string("use_double_precision") == key) {
810 const bool useDoublePrecision =
811 (std::string("1") == value || std::string("true") == value);
812 if (mps) mps->SetDataType(useDoublePrecision);
813 if (tn) tn->SetDataType(useDoublePrecision);
814 if (state) state->SetDataType(useDoublePrecision);
815 if (densityMatrix) densityMatrix->SetDataType(useDoublePrecision);
816 if (mpo) mpo->SetDataType(useDoublePrecision);
817 }
818
819 if (pp) {
820 if (std::string("pauli_propagator_coefficient_threshold") == key) {
821 const double coefficientThreshold = std::stod(value);
822 pp->SetCoefficientTruncationCutoff(coefficientThreshold);
823 } else if (std::string("pauli_propagator_pauli_weight_threshold") ==
824 key) {
825 const double pauliWeightThreshold = std::stod(value);
826 pp->SetWeightTruncationCutoff(pauliWeightThreshold);
827 } else if (std::string("pauli_propagator_steps_between_trims") == key) {
828 const int stepsBetweenTrims = std::stoi(value);
829 pp->SetNumGatesBetweenTruncations(stepsBetweenTrims);
830 } else if (std::string("pauli_propagator_num_gates_between_deduplications") ==
831 key) {
832 const int numGatesBetweenDeduplications = std::stoi(value);
833 pp->SetNumGatesBetweenDeduplications(numGatesBetweenDeduplications);
834 }
835 }
836 }
837
845 std::string GetConfiguration(const char *key) const override {
846 if (!key) return {};
847 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
848 if (!svdGroup.empty()) {
849 if (svdGroup == key) {
850 const auto readGesvd = [](const auto& backend) {
851 return !backend->GetGesvdJ() && !backend->GetGesvdP() &&
852 !backend->GetGesvdR();
853 };
854 if (svdGroup == "matrix_product_state_use_gesvd" && mps)
855 return readGesvd(mps) ? "true" : "false";
856 if (svdGroup == "matrix_product_operator_use_gesvd" && mpo)
857 return readGesvd(mpo) ? "true" : "false";
858 if (svdGroup == "tensor_network_use_gesvd" && tn)
859 return readGesvd(tn) ? "true" : "false";
860 }
861 const char algorithm = std::string(key).back();
862 const auto read = [algorithm](const auto& backend) {
863 if (algorithm == 'j') return backend->GetGesvdJ();
864 if (algorithm == 'p') return backend->GetGesvdP();
865 return backend->GetGesvdR();
866 };
867 if (svdGroup == "matrix_product_state_use_gesvd" && mps)
868 return read(mps) ? "true" : "false";
869 if (svdGroup == "matrix_product_operator_use_gesvd" && mpo)
870 return read(mpo) ? "true" : "false";
871 if (svdGroup == "tensor_network_use_gesvd" && tn)
872 return read(tn) ? "true" : "false";
873 }
874 if (std::string("method") == key) {
875 switch (simulationType) {
876 case SimulationType::kStatevector:
877 return "statevector";
878 case SimulationType::kMatrixProductState:
879 return "matrix_product_state";
880 case SimulationType::kDensityMatrix:
881 return "density_matrix";
882 case SimulationType::kMatrixProductOperator:
883 return "matrix_product_operator";
884 case SimulationType::kTensorNetwork:
885 return "tensor_network";
886 case SimulationType::kPauliPropagator:
887 return "pauli_propagator";
888 default:
889 return "other";
890 }
891 }
892
893 return configuration.GetConfiguration(key);
894 }
895
903 size_t AllocateQubits(size_t num_qubits) override {
904 if ((simulationType == SimulationType::kStatevector && state) ||
905 (simulationType == SimulationType::kDensityMatrix && densityMatrix) ||
906 (simulationType == SimulationType::kMatrixProductOperator && mpo) ||
907 (simulationType == SimulationType::kMatrixProductState && mps) ||
908 (simulationType == SimulationType::kPauliPropagator && pp))
909 return 0;
910
911 const size_t oldNrQubits = nrQubits;
912 nrQubits += num_qubits;
913
914 return oldNrQubits;
915 }
916
923 size_t GetNumberOfQubits() const override { return nrQubits; }
924
932 void Clear() override {
933 state = nullptr;
934 densityMatrix = nullptr;
935 mpo = nullptr;
936 mps = nullptr;
937 tn = nullptr;
938 pp = nullptr;
939 nrQubits = 0;
940 dummySim = nullptr;
941 upcomingGateIndex = 0;
942 upcomingGates.clear();
943 }
944
955 size_t Measure(const Types::qubits_vector &qubits) override {
956 // TODO: this is inefficient, maybe implement it better in gpu sim
957 // for now it has the possibility of measuring a qubits interval, but not a
958 // list of qubits
959 if (qubits.size() > sizeof(size_t) * 8)
960 std::cerr
961 << "Warning: The number of qubits to measure is larger than the "
962 "number of bits in the size_t type, the outcome will be undefined"
963 << std::endl;
964
965 size_t res = 0;
966 size_t mask = 1ULL;
967
968 DontNotify();
969 if (simulationType == SimulationType::kStatevector) {
970 // TODO: measure all qubits in one shot?
971 for (size_t qubit : qubits) {
972 if (state->MeasureQubitCollapse(static_cast<int>(qubit))) res |= mask;
973 mask <<= 1;
974 }
975 } else if (simulationType == SimulationType::kDensityMatrix) {
976 for (size_t qubit : qubits) {
977 if (densityMatrix->Measure(static_cast<unsigned int>(qubit))) res |= mask;
978 mask <<= 1;
979 }
980 } else if (simulationType == SimulationType::kMatrixProductOperator) {
981 for (size_t qubit : qubits) {
982 if (mpo->Measure(static_cast<unsigned int>(qubit))) res |= mask;
983 mask <<= 1;
984 }
985 } else if (simulationType == SimulationType::kMatrixProductState) {
986 // TODO: measure all qubits in one shot?
987 for (size_t qubit : qubits) {
988 if (mps->Measure(static_cast<unsigned int>(qubit))) res |= mask;
989 mask <<= 1;
990 }
991 } else if (simulationType == SimulationType::kTensorNetwork) {
992 // TODO: measure all qubits in one shot?
993 for (size_t qubit : qubits) {
994 if (tn->Measure(static_cast<unsigned int>(qubit))) res |= mask;
995 mask <<= 1;
996 }
997 } else if (simulationType == SimulationType::kPauliPropagator) {
998 // TODO: measure all qubits in one shot?
999 for (size_t qubit : qubits) {
1000 if (pp->MeasureQubit(static_cast<int>(qubit))) res |= mask;
1001 mask <<= 1;
1002 }
1003 }
1004
1005 Notify();
1006 NotifyObservers(qubits);
1007
1008 return res;
1009 }
1010
1017 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
1018 std::vector<bool> res(qubits.size(), false);
1019
1020 DontNotify();
1021 if (simulationType == SimulationType::kStatevector) {
1022 for (size_t i = 0; i < qubits.size(); ++i)
1023 res[i] = state->MeasureQubitCollapse(static_cast<int>(qubits[i]));
1024 } else if (simulationType == SimulationType::kDensityMatrix) {
1025 for (size_t i = 0; i < qubits.size(); ++i)
1026 res[i] = densityMatrix->Measure(qubits[i]);
1027 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1028 for (size_t i = 0; i < qubits.size(); ++i)
1029 res[i] = mpo->Measure(static_cast<unsigned int>(qubits[i]));
1030 } else if (simulationType == SimulationType::kMatrixProductState) {
1031 for (size_t i = 0; i < qubits.size(); ++i)
1032 res[i] = mps->Measure(static_cast<unsigned int>(qubits[i]));
1033 } else if (simulationType == SimulationType::kTensorNetwork) {
1034 for (size_t i = 0; i < qubits.size(); ++i)
1035 res[i] = tn->Measure(static_cast<unsigned int>(qubits[i]));
1036 } else if (simulationType == SimulationType::kPauliPropagator) {
1037 for (size_t i = 0; i < qubits.size(); ++i)
1038 res[i] = pp->MeasureQubit(static_cast<int>(qubits[i]));
1039 }
1040 Notify();
1041 NotifyObservers(qubits);
1042
1043 return res;
1044 }
1045
1052 void ApplyReset(const Types::qubits_vector &qubits) override {
1053 DontNotify();
1054 if (simulationType == SimulationType::kStatevector) {
1055 for (size_t qubit : qubits)
1056 if (state->MeasureQubitCollapse(static_cast<int>(qubit)))
1057 state->ApplyX(static_cast<int>(qubit));
1058 } else if (simulationType == SimulationType::kDensityMatrix) {
1059 for (size_t qubit : qubits) densityMatrix->ApplyReset(qubit);
1060 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1061 for (size_t qubit : qubits)
1062 mpo->ApplyReset(static_cast<int>(qubit));
1063 } else if (simulationType == SimulationType::kMatrixProductState) {
1064 for (size_t qubit : qubits)
1065 if (mps->Measure(static_cast<unsigned int>(qubit)))
1066 mps->ApplyX(static_cast<unsigned int>(qubit));
1067 } else if (simulationType == SimulationType::kTensorNetwork) {
1068 for (size_t qubit : qubits)
1069 if (tn->Measure(static_cast<unsigned int>(qubit)))
1070 tn->ApplyX(static_cast<unsigned int>(qubit));
1071 } else if (simulationType == SimulationType::kPauliPropagator) {
1072 for (size_t qubit : qubits)
1073 if (pp->MeasureQubit(static_cast<int>(qubit)))
1074 pp->ApplyX(static_cast<int>(qubit));
1075 }
1076
1077 Notify();
1078 NotifyObservers(qubits);
1079 }
1080
1081 bool SupportsQuantumChannels() const override {
1082 return simulationType == SimulationType::kDensityMatrix ||
1083 simulationType == SimulationType::kMatrixProductOperator;
1084 }
1085
1086 void ApplyQuantumChannel(const Types::qubits_vector& targets,
1087 const QuantumChannel& channel) override {
1088 if (!densityMatrix && !mpo)
1089 throw std::runtime_error(
1090 "GPU quantum channels require an initialized density matrix or "
1091 "matrix product operator");
1092 if (targets.size() != channel.GetNumberOfQubits() || targets.empty() ||
1093 targets.size() > 2)
1094 throw std::invalid_argument(
1095 "GPU density matrices and matrix product operators support one- "
1096 "and two-qubit local channels");
1097 std::vector<int> gpuTargets;
1098 gpuTargets.reserve(targets.size());
1099 for (auto target : targets) {
1100 if (target >= nrQubits ||
1101 std::find(gpuTargets.begin(), gpuTargets.end(), target) !=
1102 gpuTargets.end())
1103 throw std::invalid_argument("Invalid GPU quantum-channel target");
1104 gpuTargets.push_back(static_cast<int>(target));
1105 }
1106 const auto& kraus = channel.GetKrausOperators();
1107 std::vector<double> interleaved;
1108 interleaved.reserve(kraus.size() * kraus.front().size() * 2);
1109 for (const auto& op : kraus)
1110 for (Eigen::Index i = 0; i < op.size(); ++i) {
1111 interleaved.push_back(op.data()[i].real());
1112 interleaved.push_back(op.data()[i].imag());
1113 }
1114 const bool applied =
1115 densityMatrix
1116 ? densityMatrix->ApplyKraus(gpuTargets, kraus.size(),
1117 interleaved.data())
1118 : mpo->ApplyKraus(gpuTargets, kraus.size(), interleaved.data());
1119 if (!applied)
1120 throw std::runtime_error(
1121 "GPU density-matrix/matrix-product-operator channel application "
1122 "failed");
1123 NotifyObservers(targets);
1124 }
1125
1126 std::complex<double> DensityMatrixTrace() const override {
1127 if (densityMatrix) return densityMatrix->Trace();
1128 if (mpo) return mpo->Trace();
1129 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or matrix_product_operator");
1130 }
1131 double DensityMatrixPurity() const override {
1132 if (densityMatrix) return densityMatrix->Purity();
1133 if (mpo) return mpo->Purity();
1134 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or matrix_product_operator");
1135 }
1136 std::complex<double> DensityMatrixTraceOfSquare() const override {
1137 if (mpo) return mpo->TraceOfSquare();
1138 if (densityMatrix) {
1139 const double tr = densityMatrix->Trace();
1140 return densityMatrix->Purity() * tr * tr;
1141 }
1142 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or matrix_product_operator");
1143 }
1144 std::complex<double> DensityMatrixOverlap(const IState &other) const override {
1145 const auto *rhs = dynamic_cast<const GpuState *>(&other);
1146 if (!rhs) throw std::invalid_argument("Density-matrix overlap requires matching GPU backends");
1147 if (densityMatrix && rhs->densityMatrix)
1148 return densityMatrix->HilbertSchmidtOverlap(*rhs->densityMatrix);
1149 if (mpo && rhs->mpo) return mpo->HilbertSchmidtOverlap(*rhs->mpo);
1150 throw std::invalid_argument("Density-matrix overlap requires two density matrices or two MPOs");
1151 }
1152 double DensityMatrixHermiticityResidual() const override {
1153 if (mpo) return mpo->HermiticityResidual();
1154 if (densityMatrix) return densityMatrix->IsHermitian() ? 0. : std::numeric_limits<double>::infinity();
1155 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or matrix_product_operator");
1156 }
1157 bool IsDensityMatrixHermitian(double eps = 1e-10) const override {
1158 if (densityMatrix) return densityMatrix->IsHermitian(eps);
1159 if (mpo) return mpo->IsHermitian(eps);
1160 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or matrix_product_operator");
1161 }
1162 Eigen::MatrixXcd PartialTrace(const Types::qubits_vector &qubits) const override {
1163 std::vector<int> keep(qubits.begin(), qubits.end());
1164 const auto values = densityMatrix ? densityMatrix->PartialTrace(keep) :
1165 mpo ? mpo->PartialTrace(keep) : throw std::runtime_error("GPU partial trace requires a mixed-state backend");
1166 const Eigen::Index dim = static_cast<Eigen::Index>(size_t{1} << keep.size());
1167 return Eigen::Map<const Eigen::MatrixXcd>(values.data(), dim, dim);
1168 }
1169 double FidelityWithStatevector(const Eigen::VectorXcd &psi) const override {
1170 std::vector<double> raw(2 * static_cast<size_t>(psi.size()));
1171 for (Eigen::Index i = 0; i < psi.size(); ++i) { raw[2*i] = psi[i].real(); raw[2*i+1] = psi[i].imag(); }
1172 if (densityMatrix) return densityMatrix->FidelityWithStatevector(raw.data());
1173 if (mpo) return mpo->FidelityWithStatevector(raw.data());
1174 throw std::runtime_error("GPU mixed-state fidelity requires density_matrix or matrix_product_operator");
1175 }
1176 void RestoreDensityMatrixTrace() override {
1177 if (!mpo) throw std::runtime_error("Trace restoration is only available for GPU MPO");
1178 mpo->RestoreTrace();
1179 }
1180 void HermitizeDensityMatrix() override {
1181 if (!mpo) throw std::runtime_error("Hermitization is only available for GPU MPO");
1182 mpo->Hermitize();
1183 }
1184 void TrimMatrixProductOperator() override {
1185 if (!mpo) throw std::runtime_error("GPU MPO is not initialized");
1186 mpo->Trim();
1187 }
1188 void ReCanonicalizeMatrixProductOperator() override {
1189 if (!mpo) throw std::runtime_error("GPU MPO is not initialized");
1190 mpo->ReCanonicalize();
1191 }
1192
1204 double Probability(Types::qubit_t outcome) override {
1205 if (simulationType == SimulationType::kStatevector)
1206 return state->BasisStateProbability(outcome);
1207 else if (simulationType == SimulationType::kDensityMatrix)
1208 return densityMatrix->Probability(outcome);
1209 else if (simulationType == SimulationType::kMatrixProductOperator)
1210 return mpo->Probability(outcome);
1211 else if (simulationType == SimulationType::kMatrixProductState ||
1212 simulationType == SimulationType::kTensorNetwork) {
1213 const auto ampl = Amplitude(outcome);
1214 return std::norm(ampl);
1215 } else if (simulationType == SimulationType::kPauliPropagator) {
1216 return pp->Probability(outcome);
1217 }
1218
1219 return 0.0;
1220 }
1221
1232 std::complex<double> Amplitude(Types::qubit_t outcome) override {
1233 double real = 0.0;
1234 double imag = 0.0;
1235
1236 if (simulationType == SimulationType::kStatevector)
1237 state->Amplitude(outcome, &real, &imag);
1238 else if (simulationType == SimulationType::kDensityMatrix)
1239 throw std::runtime_error(
1240 "GpuState::Amplitude: Amplitudes are not defined for density matrices.");
1241 else if (simulationType == SimulationType::kMatrixProductOperator)
1242 throw std::runtime_error(
1243 "GpuState::Amplitude: Amplitudes are not defined for matrix "
1244 "product operators.");
1245 else if (simulationType == SimulationType::kMatrixProductState ||
1246 simulationType == SimulationType::kTensorNetwork) {
1247 std::vector<long int> fixedValues(nrQubits);
1248 for (size_t i = 0; i < nrQubits; ++i)
1249 fixedValues[i] = (outcome & (1ULL << i)) ? 1 : 0;
1250 if (simulationType == SimulationType::kMatrixProductState)
1251 mps->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
1252 else if (simulationType == SimulationType::kTensorNetwork)
1253 tn->Amplitude(nrQubits, fixedValues.data(), &real, &imag);
1254 } else if (simulationType == SimulationType::kPauliPropagator) {
1255 // Pauli propagator does not support amplitude calculation
1256 throw std::runtime_error(
1257 "GpuState::Amplitude: Invalid simulation type for amplitude "
1258 "calculation.");
1259 }
1260
1261 return std::complex<double>(real, imag);
1262 }
1263
1277 std::complex<double> ProjectOnZero() override {
1278 if (simulationType == SimulationType::kMatrixProductState)
1279 return mps->ProjectOnZero();
1280
1281 return Amplitude(0);
1282 }
1283
1294 std::vector<double> AllProbabilities() override {
1295 if (nrQubits == 0) return {};
1296 const size_t numStates = 1ULL << nrQubits;
1297 std::vector<double> result(numStates);
1298
1299 if (simulationType == SimulationType::kStatevector)
1300 state->AllProbabilities(result.data());
1301 else if (simulationType == SimulationType::kDensityMatrix)
1302 densityMatrix->AllProbabilities(result.data());
1303 else if (simulationType == SimulationType::kMatrixProductOperator)
1304 mpo->AllProbabilities(result.data());
1305 else if (simulationType == SimulationType::kMatrixProductState ||
1306 simulationType == SimulationType::kTensorNetwork) {
1307 // this is very slow, it should be used only for tests!
1308 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
1309 const auto val = Amplitude(i);
1310 result[i] = std::norm(std::complex<double>(val.real(), val.imag()));
1311 }
1312 } else if (simulationType == SimulationType::kPauliPropagator) {
1313 // this is very slow, it should be used only for tests!
1314 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i) {
1315 result[i] = pp->Probability(i);
1316 }
1317 }
1318
1319 return result;
1320 }
1321
1333 std::vector<double> Probabilities(
1334 const Types::qubits_vector &qubits) override {
1335 std::vector<double> result(qubits.size());
1336
1337 if (simulationType == SimulationType::kStatevector) {
1338 for (size_t i = 0; i < qubits.size(); ++i)
1339 result[i] = state->BasisStateProbability(qubits[i]);
1340 } else if (simulationType == SimulationType::kDensityMatrix) {
1341 for (size_t i = 0; i < qubits.size(); ++i)
1342 result[i] = densityMatrix->Probability(qubits[i]);
1343 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1344 for (size_t i = 0; i < qubits.size(); ++i)
1345 result[i] = mpo->Probability(qubits[i]);
1346 } else if (simulationType == SimulationType::kMatrixProductState ||
1347 simulationType == SimulationType::kTensorNetwork) {
1348 for (size_t i = 0; i < qubits.size(); ++i) {
1349 const auto ampl = Amplitude(qubits[i]);
1350 result[i] = std::norm(ampl);
1351 }
1352 } else if (simulationType == SimulationType::kPauliPropagator) {
1353 for (size_t i = 0; i < qubits.size(); ++i)
1354 result[i] = pp->Probability(qubits[i]);
1355 }
1356
1357 return result;
1358 }
1359
1376 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
1377 const Types::qubits_vector &qubits, size_t shots = 1000) override {
1378 if (qubits.empty() || shots == 0) return {};
1379
1380 if (qubits.size() > sizeof(Types::qubit_t) * 8)
1381 std::cerr
1382 << "Warning: The number of qubits to measure is larger than the "
1383 "number of bits in the Types::qubit_t type, the outcome will be "
1384 "undefined"
1385 << std::endl;
1386
1387 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
1388
1389 DontNotify();
1390
1391 if (simulationType == SimulationType::kStatevector) {
1392 std::vector<long int> samples(shots);
1393 state->SampleAll(shots, samples.data());
1394
1395 for (auto outcome : samples) {
1396 // qubits might not be in order, translate the outcome to the correct
1397 // order
1398 Types::qubit_t translatedOutcome = 0;
1399 Types::qubit_t mask = 1ULL;
1400 for (size_t i = 0; i < qubits.size(); ++i) {
1401 if (outcome & (1ULL << qubits[i])) translatedOutcome |= mask;
1402 mask <<= 1;
1403 }
1404 ++result[translatedOutcome];
1405 }
1406 } else if (simulationType == SimulationType::kDensityMatrix) {
1407 std::vector<long int> samples(shots);
1408 if (!densityMatrix->SampleAll(shots, samples.data())) {
1409 Notify();
1410 throw std::runtime_error(
1411 "GpuState::SampleCounts: Density-matrix sampling failed.");
1412 }
1413 for (auto outcome : samples) {
1414 Types::qubit_t translatedOutcome = 0;
1415 for (size_t i = 0; i < qubits.size(); ++i)
1416 if (outcome & (1ULL << qubits[i])) translatedOutcome |= 1ULL << i;
1417 ++result[translatedOutcome];
1418 }
1419 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1420 std::vector<long int> samples(shots);
1421 if (!mpo->SampleAll(shots, samples.data())) {
1422 Notify();
1423 throw std::runtime_error(
1424 "GpuState::SampleCounts: Matrix-product-operator sampling "
1425 "failed.");
1426 }
1427 for (auto outcome : samples) {
1428 Types::qubit_t translatedOutcome = 0;
1429 for (size_t i = 0; i < qubits.size(); ++i)
1430 if (outcome & (1ULL << qubits[i])) translatedOutcome |= 1ULL << i;
1431 ++result[translatedOutcome];
1432 }
1433 } else if (simulationType == SimulationType::kMatrixProductState) {
1434 std::unordered_map<std::vector<bool>, int64_t> *map =
1435 mps->GetMapForSample();
1436
1437 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
1438
1439 mps->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
1440 const auto positions = SampleBitPositions(qubits);
1441
1442 // put the results in the result map
1443 for (const auto &[meas, cnt] : *map) {
1444 Types::qubit_t outcome = 0;
1445 Types::qubit_t mask = 1ULL;
1446 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
1447 if (meas[positions[q]]) outcome |= mask;
1448 mask <<= 1;
1449 }
1450
1451 result[outcome] += cnt;
1452 }
1453
1454 mps->FreeMapForSample(map);
1455 } else if (simulationType == SimulationType::kTensorNetwork) {
1456 std::unordered_map<std::vector<bool>, int64_t> *map =
1457 tn->GetMapForSample();
1458 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
1459 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
1460 const auto positions = SampleBitPositions(qubits);
1461 // put the results in the result map
1462 for (const auto &[meas, cnt] : *map) {
1463 Types::qubit_t outcome = 0;
1464 Types::qubit_t mask = 1ULL;
1465 for (Types::qubit_t q = 0; q < qubits.size(); ++q) {
1466 if (meas[positions[q]]) outcome |= mask;
1467 mask <<= 1;
1468 }
1469 result[outcome] += cnt;
1470 }
1471 tn->FreeMapForSample(map);
1472 } else if (simulationType == SimulationType::kPauliPropagator) {
1473 std::vector<int> qb(qubits.begin(), qubits.end());
1474 for (size_t shot = 0; shot < shots; ++shot) {
1475 size_t meas = 0;
1476 auto res = pp->SampleQubits(qb);
1477 for (size_t i = 0; i < qubits.size(); ++i) {
1478 if (res[i]) meas |= (1ULL << i);
1479 }
1480 ++result[meas];
1481 }
1482 }
1483
1484 Notify();
1485 NotifyObservers(qubits);
1486
1487 return result;
1488 }
1489
1503 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
1504 const Types::qubits_vector &qubits, size_t shots = 1000) override {
1505 if (qubits.empty() || shots == 0) return {};
1506
1507 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
1508
1509 DontNotify();
1510
1511 if (simulationType == SimulationType::kStatevector) {
1512 std::vector<long int> samples(shots);
1513 state->SampleAll(shots, samples.data());
1514
1515 std::vector<bool> outcomeVec(qubits.size());
1516 for (auto outcome : samples) {
1517 for (size_t i = 0; i < qubits.size(); ++i)
1518 outcomeVec[i] = ((outcome >> qubits[i]) & 1) == 1;
1519 ++result[outcomeVec];
1520 }
1521 } else if (simulationType == SimulationType::kDensityMatrix) {
1522 std::vector<long int> samples(shots);
1523 if (!densityMatrix->SampleAll(shots, samples.data())) {
1524 Notify();
1525 throw std::runtime_error(
1526 "GpuState::SampleCountsMany: Density-matrix sampling failed.");
1527 }
1528 std::vector<bool> outcomeVec(qubits.size());
1529 for (auto outcome : samples) {
1530 for (size_t i = 0; i < qubits.size(); ++i)
1531 outcomeVec[i] = ((outcome >> qubits[i]) & 1) != 0;
1532 ++result[outcomeVec];
1533 }
1534 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1535 std::vector<long int> samples(shots);
1536 if (!mpo->SampleAll(shots, samples.data())) {
1537 Notify();
1538 throw std::runtime_error(
1539 "GpuState::SampleCountsMany: Matrix-product-operator sampling "
1540 "failed.");
1541 }
1542 std::vector<bool> outcomeVec(qubits.size());
1543 for (auto outcome : samples) {
1544 for (size_t i = 0; i < qubits.size(); ++i)
1545 outcomeVec[i] = ((outcome >> qubits[i]) & 1) != 0;
1546 ++result[outcomeVec];
1547 }
1548 } else if (simulationType == SimulationType::kMatrixProductState) {
1549 std::unordered_map<std::vector<bool>, int64_t> *map =
1550 mps->GetMapForSample();
1551
1552 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
1553 mps->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
1554 const auto positions = SampleBitPositions(qubits);
1555
1556 // put the results in the result map
1557 for (const auto &[meas, cnt] : *map) {
1558 std::vector<bool> ordered(qubits.size());
1559 for (size_t q = 0; q < qubits.size(); ++q) ordered[q] = meas[positions[q]];
1560 result[ordered] += cnt;
1561 }
1562
1563 mps->FreeMapForSample(map);
1564 } else if (simulationType == SimulationType::kTensorNetwork) {
1565 std::unordered_map<std::vector<bool>, int64_t> *map =
1566 tn->GetMapForSample();
1567 std::vector<unsigned int> qubitsIndices(qubits.begin(), qubits.end());
1568 tn->Sample(shots, qubitsIndices.size(), qubitsIndices.data(), map);
1569 const auto positions = SampleBitPositions(qubits);
1570 // put the results in the result map
1571 for (const auto &[meas, cnt] : *map) {
1572 std::vector<bool> ordered(qubits.size());
1573 for (size_t q = 0; q < qubits.size(); ++q) ordered[q] = meas[positions[q]];
1574 result[ordered] += cnt;
1575 }
1576 tn->FreeMapForSample(map);
1577 } else if (simulationType == SimulationType::kPauliPropagator) {
1578 std::vector<int> qb(qubits.begin(), qubits.end());
1579 for (size_t shot = 0; shot < shots; ++shot) {
1580 const auto res = pp->SampleQubits(qb);
1581 ++result[res];
1582 }
1583 }
1584
1585 Notify();
1586 NotifyObservers(qubits);
1587
1588 return result;
1589 }
1590
1602 double ExpectationValue(const std::string &pauliString) override {
1603 double result = 0.0;
1604
1605 if (simulationType == SimulationType::kStatevector)
1606 result = state->ExpectationValue(pauliString);
1607 else if (simulationType == SimulationType::kDensityMatrix)
1608 result = densityMatrix->ExpectationValue(pauliString);
1609 else if (simulationType == SimulationType::kMatrixProductOperator)
1610 result = mpo->ExpectationValue(pauliString);
1611 else if (simulationType == SimulationType::kMatrixProductState)
1612 result = mps->ExpectationValue(pauliString);
1613 else if (simulationType == SimulationType::kTensorNetwork)
1614 result = tn->ExpectationValue(pauliString);
1615 else if (simulationType == SimulationType::kPauliPropagator)
1616 result = pp->ExpectationValue(pauliString);
1617 else
1618 throw std::runtime_error(
1619 "GpuState::ExpectationValue: Invalid simulation type for expectation "
1620 "value calculation.");
1621
1622 return result;
1623 }
1624
1632 int GetGpuDevice() const override {
1633 if (state) return state->GetGpuDevice();
1634 if (densityMatrix) return densityMatrix->GetGpuDevice();
1635 if (mpo) return mpo->GetGpuDevice();
1636 if (mps) return mps->GetGpuDevice();
1637 if (tn) return tn->GetGpuDevice();
1638 if (pp) return pp->GetGpuDevice();
1639 return -1;
1640 }
1641
1642 SimulatorType GetType() const override { return SimulatorType::kGpuSim; }
1643
1652 SimulationType GetSimulationType() const override { return simulationType; }
1653
1662 void Flush() override {}
1663
1674 void SaveStateToInternalDestructive() override {
1675 if (simulationType == SimulationType::kStatevector)
1676 state->SaveStateDestructive();
1677 else if (simulationType == SimulationType::kPauliPropagator)
1678 return;
1679 else
1680 throw std::runtime_error(
1681 "GpuState::SaveStateToInternalDestructive: Invalid simulation type "
1682 "for saving the state destructively.");
1683 }
1684
1691 void RestoreInternalDestructiveSavedState() override {
1692 if (simulationType == SimulationType::kStatevector)
1693 state->RestoreStateFreeSaved();
1694 else if (simulationType == SimulationType::kPauliPropagator)
1695 return;
1696 else
1697 throw std::runtime_error(
1698 "GpuState::RestoreInternalDestructiveSavedState: Invalid simulation "
1699 "type for restoring the state destructively.");
1700 }
1701
1710 void SaveState() override {
1711 if (simulationType == SimulationType::kStatevector)
1712 state->SaveState();
1713 else if (simulationType == SimulationType::kDensityMatrix)
1714 densityMatrix->SaveState();
1715 else if (simulationType == SimulationType::kMatrixProductOperator)
1716 mpo->SaveState();
1717 else if (simulationType == SimulationType::kMatrixProductState)
1718 mps->SaveState();
1719 else if (simulationType == SimulationType::kTensorNetwork)
1720 tn->SaveState();
1721 else if (simulationType == SimulationType::kPauliPropagator)
1722 pp->SaveState();
1723 }
1724
1732 void RestoreState() override {
1733 if (simulationType == SimulationType::kStatevector)
1734 state->RestoreStateNoFreeSaved();
1735 else if (simulationType == SimulationType::kDensityMatrix)
1736 densityMatrix->RestoreState();
1737 else if (simulationType == SimulationType::kMatrixProductOperator)
1738 mpo->RestoreState();
1739 else if (simulationType == SimulationType::kMatrixProductState)
1740 mps->RestoreState();
1741 else if (simulationType == SimulationType::kTensorNetwork)
1742 tn->RestoreState();
1743 else if (simulationType == SimulationType::kPauliPropagator)
1744 pp->RestoreState();
1745 }
1746
1754 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
1755 return Amplitude(outcome);
1756 }
1757
1766 void SetMultithreading(bool multithreading = true) override {
1767 // don't do anything here, the multithreading is always enabled
1768 }
1769
1777 bool GetMultithreading() const override { return true; }
1778
1789 bool IsQcsim() const override { return false; }
1790
1808 if (simulationType == SimulationType::kStatevector)
1809 return state->MeasureAllQubitsNoCollapse();
1810 else if (simulationType == SimulationType::kDensityMatrix) {
1811 std::vector<long int> samples(1);
1812 if (!densityMatrix->SampleAll(1, samples.data()))
1813 throw std::runtime_error(
1814 "GpuState::MeasureNoCollapse: Density-matrix sampling failed.");
1815 return static_cast<Types::qubit_t>(samples.front());
1816 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1817 std::vector<long int> samples(1);
1818 if (!mpo->SampleAll(1, samples.data()))
1819 throw std::runtime_error(
1820 "GpuState::MeasureNoCollapse: Matrix-product-operator sampling "
1821 "failed.");
1822 return static_cast<Types::qubit_t>(samples.front());
1823 } else if (simulationType == SimulationType::kMatrixProductState ||
1824 simulationType == SimulationType::kTensorNetwork ||
1825 simulationType == SimulationType::kPauliPropagator) {
1826 if (nrQubits > sizeof(Types::qubit_t) * 8)
1827 std::cerr
1828 << "Warning: The number of qubits to measure is larger than the "
1829 "number of bits in the Types::qubit_t type, the outcome will be "
1830 "undefined"
1831 << std::endl;
1832
1833 Types::qubits_vector fixedValues(nrQubits);
1834 std::iota(fixedValues.begin(), fixedValues.end(), 0);
1835 const auto res = SampleCounts(fixedValues, 1);
1836 if (res.empty()) return 0;
1837 return res.begin()
1838 ->first; // return the first outcome, as it is the only one
1839 }
1840
1841 throw std::runtime_error(
1842 "GpuState::MeasureNoCollapse: Invalid simulation type for measuring "
1843 "all the qubits without collapsing the state.");
1844
1845 return 0;
1846 }
1847
1862 std::vector<bool> MeasureNoCollapseMany() override {
1863 if (simulationType == SimulationType::kStatevector) {
1864 const auto meas = state->MeasureAllQubitsNoCollapse();
1865 std::vector<bool> result(nrQubits, false);
1866 for (size_t i = 0; i < nrQubits; ++i) result[i] = ((meas >> i) & 1) == 1;
1867 return result;
1868 } else if (simulationType == SimulationType::kDensityMatrix) {
1869 std::vector<long int> samples(1);
1870 if (!densityMatrix->SampleAll(1, samples.data()))
1871 throw std::runtime_error(
1872 "GpuState::MeasureNoCollapseMany: Density-matrix sampling failed.");
1873 std::vector<bool> result(nrQubits, false);
1874 for (size_t i = 0; i < nrQubits; ++i)
1875 result[i] = ((samples.front() >> i) & 1) != 0;
1876 return result;
1877 } else if (simulationType == SimulationType::kMatrixProductOperator) {
1878 std::vector<long int> samples(1);
1879 if (!mpo->SampleAll(1, samples.data()))
1880 throw std::runtime_error(
1881 "GpuState::MeasureNoCollapseMany: Matrix-product-operator "
1882 "sampling failed.");
1883 std::vector<bool> result(nrQubits, false);
1884 for (size_t i = 0; i < nrQubits; ++i)
1885 result[i] = ((samples.front() >> i) & 1) != 0;
1886 return result;
1887 } else if (simulationType == SimulationType::kMatrixProductState ||
1888 simulationType == SimulationType::kTensorNetwork ||
1889 simulationType == SimulationType::kPauliPropagator) {
1890 Types::qubits_vector fixedValues(nrQubits);
1891 std::iota(fixedValues.begin(), fixedValues.end(), 0);
1892 const auto res = SampleCountsMany(fixedValues, 1);
1893 if (res.empty()) return std::vector<bool>(nrQubits, false);
1894 return res.begin()
1895 ->first; // return the first outcome, as it is the only one
1896 }
1897
1898 throw std::runtime_error(
1899 "GpuState::MeasureNoCollapseMany: Invalid simulation type for "
1900 "measuring "
1901 "all the qubits without collapsing the state.");
1902
1903 return std::vector<bool>(nrQubits, false);
1904 }
1905
1912 size_t GetCurrentMaxBondDimension() const override { return curMaxBondDim; }
1913
1914
1915 const Configuration& GetConfiguration() const { return configuration; }
1916
1917 const std::unordered_map<std::string, std::string>& GetConfigMap()
1918 const override {
1919 return configuration.GetConfigMap();
1920 }
1921
1922 protected:
1923 // MPSSample/TNSample consume a set and return bits in ascending logical
1924 // qubit order. Maestro's sampling interface preserves the caller's order.
1925 static std::vector<size_t> SampleBitPositions(const Types::qubits_vector& qubits) {
1926 auto sorted = qubits;
1927 std::sort(sorted.begin(), sorted.end());
1928 sorted.erase(std::unique(sorted.begin(), sorted.end()), sorted.end());
1929 std::vector<size_t> positions;
1930 positions.reserve(qubits.size());
1931 for (const auto q : qubits)
1932 positions.push_back(std::lower_bound(sorted.begin(), sorted.end(), q) - sorted.begin());
1933 return positions;
1934 }
1935
1936 // MPO accepts both names; Configure keeps their values synchronized.
1937 // The fallback also handles configuration before the method is selected.
1938 const char* MaxBondDimensionConfigKey() const {
1939 return simulationType == SimulationType::kMatrixProductOperator &&
1940 configuration.IsSet(
1941 "matrix_product_operator_max_bond_dimension")
1942 ? "matrix_product_operator_max_bond_dimension"
1943 : "matrix_product_state_max_bond_dimension";
1944 }
1945
1946 static int64_t FindBestMeetingPosition(void* thisPtr, const int64_t* bondDims) {
1947 GpuState* self = static_cast<GpuState*>(thisPtr);
1948
1949 return self->FindBestMeetingPositionFunc(bondDims);
1950 };
1951
1952 int64_t FindBestMeetingPositionFunc(const int64_t* bondDims)
1953 {
1954 const size_t nQ = GetNumberOfQubits();
1955
1956 if (lookaheadDepth <= 0 || lookaheadDepth == std::numeric_limits<int>::max())
1957 return -1;
1958
1959 if (!dummySim || dummySim->getNrQubits() != nQ) {
1960 dummySim = std::make_unique<Simulators::MPSDummySimulator>(nQ);
1961 dummySim->SetMaxBondDimension(
1962 configuration.GetConfigurationAsInt(MaxBondDimensionConfigKey()));
1963 dummySim->setGrowthFactorGate(growthFactorGate);
1964 dummySim->setGrowthFactorSwap(growthFactorSwap);
1965 }
1966
1967 dummySim->setTotalSwappingCost(0);
1968
1969 // Convert actual bond dims to doubles
1970 std::vector<double> bondDimsD(bondDims, bondDims + nrQubits - 1);
1971 dummySim->SetCurrentBondDimensions(bondDimsD);
1972
1973 // display bond dimensions for debugging
1974#ifdef LOG_CALLBACK_INFO
1975 std::cerr << "Bond dimensions before swapping and applying the gate:";
1976 for (size_t i = 0; i < nrQubits - 1; ++i) {
1977 std::cerr << bondDims[i] << " ";
1978 }
1979 std::cerr << std::endl;
1980#endif
1981
1982 if (upcomingGates.size() <= static_cast<size_t>(upcomingGateIndex)) {
1983 return -1; // will fallback
1984 }
1985
1986 const auto &op = upcomingGates[upcomingGateIndex];
1987 const auto qbits = op->AffectedQubits();
1988
1989 if (qbits.size() != 2) {
1990 std::cerr << "Error: Meeting position callback called for a gate "
1991 "that does not have exactly 2 qubits."
1992 << std::endl;
1993
1994 return -1; // will fallback
1995 }
1996
1997#ifdef LOG_CALLBACK_INFO
1998 const auto& qmap = dummySim->getQubitsMap();
1999
2000 std::cerr << "Applying 2-qubit gate on physical qubits " << qmap[qbits[0]]
2001 << " and " << qmap[qbits[1]] << std::endl;
2002
2003 std::cerr << "Finding best meeting position for upcoming gates starting at index "
2004 << upcomingGateIndex << " with lookahead depth " << lookaheadDepth
2005 << " and heuristic depth " << lookaheadDepthWithHeuristic
2006 << std::endl;
2007
2008 std::cerr << "Affected qubits: ";
2009 for (const auto& q : qbits) std::cerr << q << " ";
2010 std::cerr << std::endl;
2011#endif
2012
2013 double bestCost = std::numeric_limits<double>::infinity();
2014 int64_t res = dummySim->FindBestMeetingPosition(
2015 upcomingGates, upcomingGateIndex, lookaheadDepth,
2016 lookaheadDepthWithHeuristic, 0, bestCost);
2017
2018#ifdef LOG_CALLBACK_INFO
2019 std::cerr << "Swapping the two qubits on position: " << res << " and "
2020 << (res + 1) << std::endl;
2021#endif
2022
2023 dummySim->SwapQubitsToPosition(qbits[0], qbits[1], res);
2024 dummySim->ApplyGate(op);
2025
2026 // display the expected bond dimensions after applying the gate for
2027 // debugging
2028
2029#ifdef LOG_CALLBACK_INFO
2030 const auto& expectedBondDims = dummySim->getCurrentBondDimensions();
2031 std::cerr << "Expected bond dimensions after swapping and applying "
2032 "the gate: ";
2033 for (size_t i = 0; i < expectedBondDims.size(); ++i) {
2034 std::cerr << expectedBondDims[i] << " ";
2035 }
2036 std::cerr << std::endl;
2037
2038 std::cerr << "Best meeting position: " << res
2039 << " with estimated cost: " << bestCost << std::endl;
2040#endif
2041
2042
2043 return res;
2044 }
2045
2046 static void BondDimCallback(void* thisPtr, const int64_t* bondDims) {
2047 GpuState* self = static_cast<GpuState*>(thisPtr);
2048
2049 return self->BondDimCallbackFunc(bondDims);
2050 }
2051
2052 void BondDimCallbackFunc(const int64_t* bondDims)
2053 {
2054 if (bondDims) {
2055 const size_t nQ = GetNumberOfQubits();
2056 for (int i = 0; i < static_cast<int>(nQ) - 1; ++i)
2057 if (static_cast<size_t>(bondDims[i]) > curMaxBondDim) curMaxBondDim = static_cast<size_t>(bondDims[i]);
2058 }
2059 }
2060
2061
2062 SimulationType simulationType =
2063 SimulationType::kStatevector;
2064 uint64_t nextSeedStream = 0;
2065
2066 std::unique_ptr<GpuLibStateVectorSim>
2067 state;
2068 std::unique_ptr<GpuDensityMatrix>
2069 densityMatrix;
2070 std::unique_ptr<GpuMPO>
2071 mpo;
2072 std::unique_ptr<GpuLibMPSSim> mps;
2073 std::unique_ptr<GpuLibTNSim> tn;
2074 std::unique_ptr<GpuPauliPropagator>
2075 pp;
2076
2077 size_t nrQubits = 0;
2078
2079 int lookaheadDepth = 0;
2080 int lookaheadDepthWithHeuristic = 0;
2081 bool useOptimalMeetingPosition = true;
2082 std::vector<std::shared_ptr<Circuits::IOperation<>>> upcomingGates;
2083 long long int upcomingGateIndex = 0;
2084 double growthFactorSwap = 1.;
2085 double growthFactorGate = 0.65;
2086
2087 std::unique_ptr<Simulators::MPSDummySimulator> dummySim;
2088
2089 // Observer that counts applied gates to track position in upcomingGates
2090 class GateCounterObserver : public ISimulatorObserver {
2091 public:
2092 GateCounterObserver(long long int &indexRef) : index(indexRef) {}
2093 void Update(const Types::qubits_vector &) override { ++index; }
2094
2095 private:
2096 long long int &index;
2097 };
2098
2099 std::shared_ptr<GateCounterObserver> gateCounterObserver;
2100 size_t curMaxBondDim = 0;
2101
2102 Configuration configuration;
2103};
2104
2105} // namespace Private
2106} // namespace Simulators
2107
2108#endif
2109#endif
2110#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)
int ApplyX(void *sim, int qubit)
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)
The operation interface.
Definition Operations.h:360
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:22
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:21