Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuMPOSimulator.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// matrix_product_operator backend. Owns exactly one native Gpu implementation.
9class GpuMPOSimulator : public GpuTensorChainSimulator
10{
11 public:
12 GpuMPOSimulator() : GpuTensorChainSimulator(true)
13 {
14 configuration.SetConfiguration("method", "matrix_product_operator");
15 configuration.SetConfiguration("matrix_product_state_max_bond_dimension", "128");
16 configuration.SetConfiguration("matrix_product_operator_max_bond_dimension", "128");
17 }
18
19 const char *MethodName() const override
20 {
21 return "matrix_product_operator";
22 }
23
24 void Initialize() override
25 {
26 RefreshRoutingSettings();
27 InvalidateDummyHistory();
28 if (nrQubits)
29 {
30 auto initializationLock = GpuLibrary::GetInstance()->LockInitialization();
31 const int gpuDevice = configuration.IsSet("gpu_device") ? Configuration::ParseGpuDevice(configuration.GetConfiguration("gpu_device"))
32 : SimulatorsFactory::ResolveGpuDevice();
33 configuration.SetConfiguration("gpu_device", std::to_string(gpuDevice));
34 if (!SimulatorsFactory::GetGpuLibrary(gpuDevice))
35 throw std::runtime_error("GpuState::Initialize: Unable to initialize GPU device " + std::to_string(gpuDevice));
36 SetSwapGatesRelabelQubits(GpuLibrary::GetInstance()->MPOSwapRelabels());
37 {
38 mpo = SimulatorsFactory::CreateGpuMPO(gpuDevice);
39 if (!mpo)
40 throw std::runtime_error("GpuState::Initialize: Failed to create the matrix product "
41 "operator state.");
42 mpo->SetCallbackContext(static_cast<GpuTensorChainSimulator *>(this));
43 curMaxBondDim = 1;
44 mpo->InstallBondSummary(&GpuTensorChainSimulator::BondSummaryCallback, &GpuTensorChainSimulator::BondDimCallback);
45 for (const auto &[key, value] : configuration.GetConfigMap())
46 if (key != "method")
47 Configure(key.c_str(), value.c_str());
48 if (!mpo->Create(nrQubits))
49 throw std::runtime_error("GpuState::Initialize: Failed to initialize the matrix product "
50 "operator state.");
51 if (!useOptimalMeetingPosition)
52 mpo->SetUseOptimalMeetingPosition(false);
53 }
54 if (GetGpuDevice() != gpuDevice)
55 throw std::runtime_error("GpuState::Initialize: GPU plugin did not confirm the requested "
56 "device; update the GPU library");
57 }
58 }
59
60 void InitializeState(size_t num_qubits, std::vector<std::complex<double>> &amplitudes) override
61 {
62 if (num_qubits == 0)
63 return;
64 Clear();
65 nrQubits = num_qubits;
66 Initialize();
67 const bool created = mpo->CreateWithState(nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
68 if (!created)
69 throw std::runtime_error("GpuState::InitializeState: Failed to initialize the state.");
70 }
71
72#ifndef NO_QISKIT_AER
73 void InitializeState(size_t num_qubits, AER::Vector<std::complex<double>> &amplitudes) override
74 {
75 if (num_qubits == 0)
76 return;
77 Clear();
78 nrQubits = num_qubits;
79 Initialize();
80 const bool created = mpo->CreateWithState(nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
81 if (!created)
82 throw std::runtime_error("GpuState::InitializeState: Failed to initialize the state.");
83 }
84
85#endif
86
87 void InitializeState(size_t num_qubits, Eigen::VectorXcd &amplitudes) override
88 {
89 if (num_qubits == 0)
90 return;
91 Clear();
92 nrQubits = num_qubits;
93 Initialize();
94 const bool created = mpo->CreateWithState(nrQubits, reinterpret_cast<const double *>(amplitudes.data()));
95 if (!created)
96 throw std::runtime_error("GpuState::InitializeState: Failed to initialize the state.");
97 }
98
99 void InitializeToBasisState(size_t num_qubits, Types::qubit_t basisState) override
100 {
101 if (num_qubits == 0)
102 return;
103 Clear();
104 nrQubits = num_qubits;
105 Initialize();
106 const bool created = mpo->CreateWithBasisState(nrQubits, static_cast<unsigned long long>(basisState));
107 if (!created)
108 throw std::runtime_error("GpuState::InitializeToBasisState: Failed to initialize the "
109 "state.");
110 }
111
112 void InitializeToBasisState(size_t num_qubits, const std::vector<bool> &basisState) override
113 {
114 if (num_qubits == 0)
115 return;
116 Clear();
117 nrQubits = num_qubits;
118 Initialize();
119 bool created = true;
120 {
121 std::vector<unsigned char> stateBits(num_qubits, 0);
122 for (size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
123 stateBits[q] = basisState[q] ? 1 : 0;
124 created = mpo->CreateWithBasisStateBits(nrQubits, stateBits);
125 }
126 if (!created)
127 throw std::runtime_error("GpuState::InitializeToBasisState: Failed to initialize the "
128 "state.");
129 }
130
131 void InitializeToMixtureOfBasisStates(size_t num_qubits, const std::vector<std::pair<Types::qubit_t, double>> &mixture) override
132 {
133 if (num_qubits == 0)
134 return;
135 Clear();
136 nrQubits = num_qubits;
137 Initialize();
138 std::vector<std::pair<unsigned long long, double>> converted;
139 converted.reserve(mixture.size());
140 for (const auto &[basisState, weight] : mixture)
141 converted.emplace_back(static_cast<unsigned long long>(basisState), weight);
142 const bool created = mpo->CreateWithMixtureOfBasisStates(nrQubits, converted);
143 if (!created)
144 throw std::runtime_error("GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
145 "the state.");
146 }
147
148 void InitializeToMixtureOfBasisStates(size_t num_qubits, const std::vector<std::pair<std::vector<bool>, double>> &mixture) override
149 {
150 if (num_qubits == 0)
151 return;
152 Clear();
153 nrQubits = num_qubits;
154 Initialize();
155 std::vector<double> weights;
156 weights.reserve(mixture.size());
157 std::vector<unsigned char> stateBitsFlat;
158 stateBitsFlat.reserve(mixture.size() * num_qubits);
159 for (const auto &[basisState, weight] : mixture)
160 {
161 weights.push_back(weight);
162 for (size_t q = 0; q < num_qubits; ++q)
163 stateBitsFlat.push_back((q < basisState.size() && basisState[q]) ? 1 : 0);
164 }
165 if (!mpo->CreateWithMixtureOfBasisStatesBits(nrQubits, stateBitsFlat, weights))
166 throw std::runtime_error("GpuState::InitializeToMixtureOfBasisStates: Failed to initialize "
167 "the state.");
168 }
169
170 void Reset() override
171 {
172 if (mpo)
173 {
174 mpo->Reset();
175 curMaxBondDim = 1;
176 }
177 upcomingGateIndex = 0;
178 ResetDummySimulator();
179 }
180
181 void SetInitialQubitsMap(const std::vector<long long int> &initialMap) override
182 {
183 if (mpo)
184 {
185 mpo->SetInitialQubitsMap(initialMap);
186 EnsureDummySimulator(initialMap.size());
187 dummySim->SetInitialQubitsMap(initialMap);
188 }
189 }
190
191 void SetUseOptimalMeetingPosition(bool enable) override
192 {
193 useOptimalMeetingPosition = enable;
194 RefreshRoutingSettings();
195 if (mpo)
196 mpo->SetUseOptimalMeetingPosition(enable);
197 RefreshRoutingCallback();
198 }
199
200 void Configure(const char *key, const char *value) override
201 {
202 if (!key || !value)
203 return;
204 if (std::string("gpu_device") == key)
205 {
206 const int device = Configuration::ParseGpuDevice(value);
207 if ((mpo) && device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
208 throw std::invalid_argument("gpu_device cannot change after initialization; clear the "
209 "simulator first");
210 configuration.SetConfiguration(key, std::to_string(device));
211 return;
212 }
213 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
214 if (!svdGroup.empty())
215 {
216 const bool enabled = Configuration::ParseGpuSvdFlag(value);
217 const bool gesvd = svdGroup == key;
218 const char algorithm = std::string(key).back();
219 const auto apply = [algorithm, enabled](auto &backend) {
220 if (!backend)
221 return true;
222 if (algorithm == 'j')
223 return backend->SetGesvdJ(enabled);
224 if (algorithm == 'p')
225 return backend->SetGesvdP(enabled);
226 return backend->SetGesvdR(enabled);
227 };
228 const auto applyGesvd = [enabled](auto &backend) {
229 if (!enabled || !backend)
230 return true;
231 return backend->SetGesvdJ(false) && backend->SetGesvdP(false) && backend->SetGesvdR(false);
232 };
233 bool applied = true;
234 if (svdGroup == "matrix_product_operator_use_gesvd")
235 applied = gesvd ? applyGesvd(mpo) : apply(mpo);
236 if (!applied)
237 throw std::runtime_error(std::string("GPU library cannot apply ") + key + "; an updated GPU plugin may be required");
238 configuration.SetConfiguration(key, value);
239 return;
240 }
241 if (std::string("method") == key)
242 {
243 ValidateMethod(value);
244 configuration.SetConfiguration(key, value);
245 return;
246 }
247 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
248 {
249 // Precision is immutable after native storage is allocated.
250 // Replayed settings take effect after Clear and Initialize.
251 const bool useDouble = Configuration::ParsePrecision(key, value);
252 if ((mpo && mpo->IsCreated()))
253 return;
254 if (mpo)
255 mpo->SetDataType(useDouble);
256 configuration.SetConfiguration(key, value);
257 return;
258 }
259 if (!configuration.WasApplied(key, value))
260 configuration.SetConfiguration(key, value);
261 RefreshRoutingSettings();
262 if (std::string("seed") == key)
263 {
264 const uint64_t seed = std::stoull(value);
265 SeedAuxiliaryRng(seed);
266 nextSeedStream = 0;
267 if (mpo)
268 mpo->SetSeed(seed);
269 return;
270 }
271 if (std::string("matrix_product_state_truncation_threshold") == key || std::string("matrix_product_operator_truncation_threshold") == key)
272 {
273 const double singularValueThreshold = std::stod(value);
274 if (singularValueThreshold >= 0.)
275 {
276 if (mpo)
277 mpo->SetCutoff(singularValueThreshold);
278 }
279 }
280 else if (std::string("matrix_product_state_truncation_mode") == key || std::string("matrix_product_operator_truncation_mode") == key)
281 {
282 int truncationMode = -1;
283 if (std::string("relative_max") == value)
284 truncationMode = 0;
285 else if (std::string("discarded_weight") == value)
286 truncationMode = 1;
287 if (truncationMode >= 0)
288 {
289 if (mpo)
290 mpo->SetTruncationMode(truncationMode);
291 }
292 }
293 else if (std::string("matrix_product_state_max_bond_dimension") == key || std::string("matrix_product_operator_max_bond_dimension") == key)
294 {
295 const long long int chi = std::stoi(value);
296 {
297 const std::string bondDimension(value);
298 for (const char *alias : {"matrix_product_state_max_bond_dimension", "matrix_product_operator_max_bond_dimension"})
299 if (!configuration.WasApplied(alias, bondDimension))
300 configuration.SetConfiguration(alias, bondDimension);
301 }
302 RefreshRoutingSettings();
303 if (mpo)
304 mpo->SetMaxExtent(chi);
305 if (chi > 0)
306 {
307 if (dummySim)
308 dummySim->SetMaxBondDimension(EffectiveLookaheadMaxBondDimension(dummySim->getNrQubits(), chi));
309 }
310 }
311 else if (std::string("matrix_product_operator_kraus_completeness_check") == key)
312 {
313 int mode = -1;
314 if (std::string(value) == "ignore")
315 mode = 0;
316 else if (std::string(value) == "warn")
317 mode = 1;
318 else if (std::string(value) == "strict")
319 mode = 2;
320 if (mpo && mode >= 0 && !mpo->SetKrausCompletenessCheck(mode))
321 throw std::runtime_error("Invalid GPU MPO Kraus completeness mode");
322 }
323 else if (std::string("matrix_product_operator_restore_trace_after_truncation") == key)
324 {
325 if (mpo && !mpo->SetRestoreTraceAfterTruncation(std::string(value) == "true" || std::string(value) == "1"))
326 throw std::runtime_error("GPU MPO truncation configuration failed");
327 }
328 else if (std::string("matrix_product_operator_hermitize_after_truncation") == key)
329 {
330 if (mpo && !mpo->SetHermitizeAfterTruncation(std::string(value) == "true" || std::string(value) == "1"))
331 throw std::runtime_error("GPU MPO truncation configuration failed");
332 }
333 }
334
335 std::string GetConfiguration(const char *key) const override
336 {
337 if (!key)
338 return {};
339 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
340 {
341 const bool useDouble = mpo ? mpo->IsDoublePrecision() : configuration.GetConfiguration("precision") == "double";
342 return std::string(key) == "precision" ? (useDouble ? "double" : "single") : (useDouble ? "1" : "0");
343 }
344 if (mpo)
345 {
346 if (std::string(key) == "matrix_product_operator_restore_trace_after_truncation")
347 return mpo->GetRestoreTraceAfterTruncation() ? "true" : "false";
348 if (std::string(key) == "matrix_product_operator_hermitize_after_truncation")
349 return mpo->GetHermitizeAfterTruncation() ? "true" : "false";
350 }
351 if (!key)
352 return {};
353 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
354 if (!svdGroup.empty())
355 {
356 if (svdGroup == key)
357 {
358 const auto readGesvd = [](const auto &backend) { return !backend->GetGesvdJ() && !backend->GetGesvdP() && !backend->GetGesvdR(); };
359 if (svdGroup == "matrix_product_operator_use_gesvd" && mpo)
360 return readGesvd(mpo) ? "true" : "false";
361 }
362 const char algorithm = std::string(key).back();
363 const auto read = [algorithm](const auto &backend) {
364 if (algorithm == 'j')
365 return backend->GetGesvdJ();
366 if (algorithm == 'p')
367 return backend->GetGesvdP();
368 return backend->GetGesvdR();
369 };
370 if (svdGroup == "matrix_product_operator_use_gesvd" && mpo)
371 return read(mpo) ? "true" : "false";
372 }
373 if (std::string("method") == key)
374 return MethodName();
375 return configuration.GetConfiguration(key);
376 }
377
378 size_t AllocateQubits(size_t num_qubits) override
379 {
380 if (mpo)
381 return 0;
382 const size_t oldNrQubits = nrQubits;
383 nrQubits += num_qubits;
384 return oldNrQubits;
385 }
386
387 void Clear() override
388 {
389 mpo = nullptr;
390 nrQubits = 0;
391 ClearDummySimulator();
392 upcomingGateIndex = 0;
393 upcomingGates.clear();
394 }
395
396 size_t Measure(const Types::qubits_vector &qubits) override
397 {
398 InvalidateDummyHistory();
399 if (mpo && qubits.size() > sizeof(size_t) * 8)
400 throw std::invalid_argument("Use MeasureMany for more than 64 measured qubits");
401 if (qubits.size() > sizeof(size_t) * 8)
402 std::cerr << "Warning: The number of qubits to measure is larger than the "
403 "number of bits in the size_t type, the outcome will be undefined"
404 << std::endl;
405 size_t res = 0;
406 size_t mask = 1ULL;
407 DontNotify();
408 {
409 for (size_t qubit : qubits)
410 {
411 if (mpo->Measure(static_cast<unsigned int>(qubit)))
412 res |= mask;
413 mask <<= 1;
414 }
415 }
416 Notify();
417 NotifyObservers(qubits);
418 return res;
419 }
420
421 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override
422 {
423 InvalidateDummyHistory();
424 std::vector<bool> res(qubits.size(), false);
425 DontNotify();
426 {
427 for (size_t i = 0; i < qubits.size(); ++i)
428 res[i] = mpo->Measure(static_cast<unsigned int>(qubits[i]));
429 }
430 Notify();
431 NotifyObservers(qubits);
432 return res;
433 }
434
435 void ApplyReset(const Types::qubits_vector &qubits) override
436 {
437 InvalidateDummyHistory();
438 DontNotify();
439 {
440 for (size_t qubit : qubits)
441 mpo->ApplyReset(static_cast<int>(qubit));
442 }
443 Notify();
444 NotifyObservers(qubits);
445 }
446
447 bool SupportsQuantumChannels() const override
448 {
449 return true;
450 }
451
452 void ApplyQuantumChannel(const Types::qubits_vector &targets, const QuantumChannel &channel) override
453 {
454 if (!mpo)
455 throw std::runtime_error("GPU quantum channels require an initialized density matrix or "
456 "matrix product operator");
457 if (targets.size() != channel.GetNumberOfQubits() || targets.empty() || targets.size() > 2)
458 throw std::invalid_argument("GPU density matrices and matrix product operators support one- "
459 "and two-qubit local channels");
460 std::vector<int> gpuTargets;
461 gpuTargets.reserve(targets.size());
462 for (auto target : targets)
463 {
464 if (target >= nrQubits || std::find(gpuTargets.begin(), gpuTargets.end(), target) != gpuTargets.end())
465 throw std::invalid_argument("Invalid GPU quantum-channel target");
466 gpuTargets.push_back(static_cast<int>(target));
467 }
468 const auto &kraus = channel.GetKrausOperators();
469 std::vector<double> interleaved;
470 interleaved.reserve(kraus.size() * kraus.front().size() * 2);
471 for (const auto &op : kraus)
472 for (Eigen::Index i = 0; i < op.size(); ++i)
473 {
474 interleaved.push_back(op.data()[i].real());
475 interleaved.push_back(op.data()[i].imag());
476 }
477 // Native channel/operator application can invoke routing before it
478 // finishes; discard old estimates both before and after that call.
479 InvalidateDummyHistory();
480 const bool applied = mpo->ApplyKraus(gpuTargets, kraus.size(), interleaved.data());
481 if (!applied)
482 throw std::runtime_error("GPU density-matrix/matrix-product-operator channel application "
483 "failed");
484 InvalidateDummyHistory();
485 NotifyObservers(targets);
486 }
487
488 double ProbabilityBits(const std::vector<bool> &bits) override
489 {
490 if (!mpo)
491 return IState::ProbabilityBits(bits);
492 return mpo->ProbabilityBits(std::vector<unsigned char>(bits.begin(), bits.end()));
493 }
494
495 std::complex<double> DensityMatrixElementBits(const std::vector<bool> &row, const std::vector<bool> &col) const override
496 {
497 if (!mpo)
498 return IState::DensityMatrixElementBits(row, col);
499 return mpo->GetElementBits(std::vector<unsigned char>(row.begin(), row.end()), std::vector<unsigned char>(col.begin(), col.end()));
500 }
501
502 Eigen::MatrixXcd GetDensityMatrix(bool normalized = true) const override
503 {
504 if (!mpo)
505 return IState::GetDensityMatrix(normalized);
506 const auto values = mpo->GetDensityMatrix(normalized);
507 const Eigen::Index dim = Eigen::Index{1} << nrQubits;
508 return Eigen::Map<const Eigen::MatrixXcd>(values.data(), dim, dim);
509 }
510
511 std::complex<double> ExpectationValueComplex(const std::string &pauli, bool normalized = true) const override
512 {
513 if (!mpo)
514 return IState::ExpectationValueComplex(pauli, normalized);
515 MPOValidation::Pauli(pauli, nrQubits);
516 return mpo->ExpectationValueComplex(pauli, normalized);
517 }
518
519 std::vector<std::complex<double>> ExpectationValuesComplex(const std::vector<std::string> &paulis, bool normalized = true) const override
520 {
521 if (!mpo)
522 return IState::ExpectationValuesComplex(paulis, normalized);
523 for (const auto &pauli : paulis)
524 MPOValidation::Pauli(pauli, nrQubits);
525 return mpo->ExpectationValuesComplex(paulis, normalized);
526 }
527
528 void ApplyOperator(const Types::qubits_vector &qubits, const Eigen::MatrixXcd &matrix, bool normalize = false) override
529 {
530 if (!mpo)
531 return IState::ApplyOperator(qubits, matrix, normalize);
532 const auto selected = MPOValidation::Operator(qubits, matrix, nrQubits);
533 if (!normalize && swapGatesRelabelQubits && qubits.size() == 2 && IsExactSwapMatrix(matrix))
534 return ApplySwap(qubits[0], qubits[1]);
535 std::vector<double> raw(2 * matrix.size());
536 for (Eigen::Index i = 0; i < matrix.size(); ++i)
537 {
538 raw[2 * i] = matrix.data()[i].real();
539 raw[2 * i + 1] = matrix.data()[i].imag();
540 }
541 // Native channel/operator application can invoke routing before it
542 // finishes; discard old estimates both before and after that call.
543 InvalidateDummyHistory();
544 if (!mpo->ApplyOperator(selected, raw.data(), normalize))
545 throw std::runtime_error("GPU MPO operator application failed");
546 InvalidateDummyHistory();
547 NotifyObservers(qubits);
548 }
549
550 void MoveAtBeginningOfChain(const Types::qubits_vector &qubits) override
551 {
552 if (!mpo)
553 return IState::MoveAtBeginningOfChain(qubits);
554 const auto selected = MPOValidation::Qubits(qubits, nrQubits);
555 if (selected.empty())
556 return;
557 mpo->MoveAtBeginningOfChain(selected);
558 InvalidateDummyHistory();
559 }
560
561 std::complex<double> DensityMatrixTrace() const override
562 {
563 if (mpo)
564 return mpo->TraceComplex();
565 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
566 "matrix_product_operator");
567 }
568
569 double DensityMatrixPurity() const override
570 {
571 if (mpo)
572 return mpo->Purity();
573 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
574 "matrix_product_operator");
575 }
576
577 std::complex<double> DensityMatrixTraceOfSquare() const override
578 {
579 if (mpo)
580 return mpo->TraceOfSquare();
581 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
582 "matrix_product_operator");
583 }
584
585 std::complex<double> DensityMatrixOverlap(const IState &other) const override
586 {
587 const auto *rhs = dynamic_cast<const GpuMPOSimulator *>(&other);
588 if (!rhs)
589 throw std::invalid_argument("Density-matrix overlap requires matching GPU backends");
590 if (mpo && rhs->mpo)
591 return mpo->HilbertSchmidtOverlap(*rhs->mpo);
592 throw std::invalid_argument("Density-matrix overlap requires two density matrices or two MPOs");
593 }
594
595 double DensityMatrixHermiticityResidual() const override
596 {
597 if (mpo)
598 return mpo->HermiticityResidual();
599 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
600 "matrix_product_operator");
601 }
602
603 bool IsDensityMatrixHermitian(double eps = 1e-10) const override
604 {
605 if (mpo)
606 return mpo->IsHermitian(eps);
607 throw std::runtime_error("GPU mixed-state diagnostics require density_matrix or "
608 "matrix_product_operator");
609 }
610
611 Eigen::MatrixXcd PartialTrace(const Types::qubits_vector &qubits) const override
612 {
613 std::vector<int> keep(qubits.begin(), qubits.end());
614 const auto values = mpo ? mpo->PartialTrace(keep) : throw std::runtime_error("GPU partial trace requires a mixed-state backend");
615 const Eigen::Index dim = static_cast<Eigen::Index>(size_t{1} << keep.size());
616 return Eigen::Map<const Eigen::MatrixXcd>(values.data(), dim, dim);
617 }
618
619 double FidelityWithStatevector(const Eigen::VectorXcd &psi) const override
620 {
621 std::vector<double> raw(2 * static_cast<size_t>(psi.size()));
622 for (Eigen::Index i = 0; i < psi.size(); ++i)
623 {
624 raw[2 * i] = psi[i].real();
625 raw[2 * i + 1] = psi[i].imag();
626 }
627 if (mpo)
628 return mpo->FidelityWithStatevector(raw.data());
629 throw std::runtime_error("GPU mixed-state fidelity requires density_matrix or "
630 "matrix_product_operator");
631 }
632
633 void RestoreDensityMatrixTrace() override
634 {
635 if (!mpo)
636 throw std::runtime_error("Trace restoration is only available for GPU MPO");
637 mpo->RestoreTrace();
638 }
639
640 void HermitizeDensityMatrix() override
641 {
642 if (!mpo)
643 throw std::runtime_error("Hermitization is only available for GPU MPO");
644 mpo->Hermitize();
645 }
646
647 void Trim() override
648 {
649 InvalidateDummyHistory();
650 if (mpo)
651 mpo->Trim();
652 else
653 throw std::runtime_error("Trim is only available for GPU MPS and MPO");
654 }
655
656 void ReCanonicalize() override
657 {
658 InvalidateDummyHistory();
659 if (mpo)
660 mpo->ReCanonicalize();
661 else
662 throw std::runtime_error("Canonicalization is only available for GPU MPS and MPO");
663 }
664
665 double Probability(Types::qubit_t outcome) override
666 {
667 if (nrQubits < sizeof(Types::qubit_t) * 8 && outcome >= (Types::qubit_t{1} << nrQubits))
668 throw std::out_of_range("GPU MPO basis state is out of range");
669 std::vector<bool> bits(nrQubits);
670 for (size_t q = 0; q < std::min(nrQubits, sizeof(Types::qubit_t) * 8); ++q)
671 bits[q] = ((outcome >> q) & 1) != 0;
672 return ProbabilityBits(bits);
673 }
674
675 std::complex<double> Amplitude(Types::qubit_t outcome) override
676 {
677 throw std::runtime_error("GpuState::Amplitude: Amplitudes are not defined for matrix "
678 "product operators.");
679 }
680
681 std::vector<double> AllProbabilities() override
682 {
683 if (nrQubits == 0)
684 return {};
685 if (nrQubits >= std::numeric_limits<size_t>::digits || (mpo && nrQubits >= 63))
686 throw std::length_error("Full probability enumeration exceeds the basis-index API; query "
687 "selected bit vectors");
688 const size_t numStates = size_t{1} << nrQubits;
689 std::vector<double> result(numStates);
690 mpo->AllProbabilities(result.data());
691 return result;
692 }
693
694 std::vector<double> Probabilities(const Types::qubits_vector &qubits) override
695 {
696 std::vector<double> result(qubits.size());
697 {
698 for (size_t i = 0; i < qubits.size(); ++i)
699 result[i] = Probability(qubits[i]);
700 }
701 return result;
702 }
703
704 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(const Types::qubits_vector &qubits, size_t shots = 1000) override
705 {
706 if (mpo)
707 {
708 if (qubits.size() > sizeof(Types::qubit_t) * 8)
709 throw std::invalid_argument("Use SampleCountsMany for more than 64 measured qubits");
710 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
711 for (const auto &entry : SampleCountsMany(qubits, shots))
712 {
713 Types::qubit_t value = 0;
714 for (size_t i = 0; i < entry.first.size(); ++i)
715 if (entry.first[i])
716 value |= Types::qubit_t{1} << i;
717 result[value] += entry.second;
718 }
719 return result;
720 }
721 if (qubits.empty() || shots == 0)
722 return {};
723 if (qubits.size() > sizeof(Types::qubit_t) * 8)
724 std::cerr << "Warning: The number of qubits to measure is larger than the "
725 "number of bits in the Types::qubit_t type, the outcome will be "
726 "undefined"
727 << std::endl;
728 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
729 DontNotify();
730 Notify();
731 NotifyObservers(qubits);
732 return result;
733 }
734
735 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(const Types::qubits_vector &qubits, size_t shots = 1000) override
736 {
737 if (mpo)
738 {
739 std::vector<unsigned int> selected;
740 for (auto q : qubits)
741 {
742 if (q >= nrQubits || q > static_cast<size_t>(std::numeric_limits<int>::max()))
743 throw std::out_of_range("GPU tensor sampled qubit is out of range");
744 selected.push_back(static_cast<unsigned int>(q));
745 }
746 if (qubits.empty() || shots == 0)
747 return {};
748 std::sort(selected.begin(), selected.end());
749 selected.erase(std::unique(selected.begin(), selected.end()), selected.end());
750 const auto counts = mpo->SampleHistogram(shots, std::move(selected));
751 // Current plugins sample without moving sites. Check the map for
752 // plugins that implement selected sampling by reordering the chain.
753 ValidateDummyHistoryMap();
754 const auto positions = SampleBitPositions(qubits);
755 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
756 for (const auto &entry : counts)
757 {
758 std::vector<bool> bits(qubits.size());
759 for (size_t i = 0; i < bits.size(); ++i)
760 bits[i] = entry.first[positions[i]];
761 result[std::move(bits)] += entry.second;
762 }
763 NotifyObservers(qubits);
764 return result;
765 }
766 if (qubits.empty() || shots == 0)
767 return {};
768 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
769 DontNotify();
770 Notify();
771 NotifyObservers(qubits);
772 return result;
773 }
774
775 double ExpectationValue(const std::string &pauliString) override
776 {
777 double result = 0.0;
778 result = mpo->ExpectationValue(pauliString);
779 return result;
780 }
781
782 std::vector<double> ExpectationValues(const std::vector<std::string> &paulis) override
783 {
784 if (!mpo)
785 return IState::ExpectationValues(paulis);
786 std::vector<double> result(paulis.size(), 0.);
787 std::vector<std::string> selected;
788 std::vector<size_t> indices;
789 for (size_t i = 0; i < paulis.size(); ++i)
790 {
791 if (paulis[i].empty())
792 {
793 result[i] = 1.;
794 continue;
795 }
796 if (paulis[i].size() > nrQubits)
797 continue;
798 auto pauli = paulis[i];
799 for (char &p : pauli)
800 p = static_cast<char>(std::toupper(static_cast<unsigned char>(p)));
801 if (pauli.find_first_not_of("IXYZ") != std::string::npos)
802 continue;
803 indices.push_back(i);
804 selected.push_back(std::move(pauli));
805 }
806 if (selected.empty())
807 return result;
808 const auto values = mpo->ExpectationValues(selected);
809 for (size_t i = 0; i < indices.size(); ++i)
810 result[indices[i]] = values[i];
811 return result;
812 }
813
814 int GetGpuDevice() const override
815 {
816 if (mpo)
817 return mpo->GetGpuDevice();
818 return -1;
819 }
820
821 SimulationType GetSimulationType() const override
822 {
823 return SimulationType::kMatrixProductOperator;
824 }
825
826 void SaveState() override
827 {
828 mpo->SaveState();
829 SaveDummyState();
830 }
831
832 void RestoreState() override
833 {
834 mpo->RestoreState();
835 RestoreDummyState();
836 }
837
839 {
840 if (mpo && nrQubits > sizeof(Types::qubit_t) * 8)
841 throw std::invalid_argument("Use MeasureNoCollapseMany for more than 64 qubits");
842 {
843 std::vector<long int> samples(1);
844 if (!mpo->SampleAll(1, samples.data()))
845 throw std::runtime_error("GpuState::MeasureNoCollapse: Matrix-product-operator sampling "
846 "failed.");
847 return static_cast<Types::qubit_t>(samples.front());
848 }
849 }
850
851 std::vector<bool> MeasureNoCollapseMany() override
852 {
853 std::vector<int> qubits(nrQubits);
854 std::iota(qubits.begin(), qubits.end(), 0);
855 const auto bits = mpo->SampleBits(1, qubits);
856 return std::vector<bool>(bits.begin(), bits.end());
857 }
858
859 void ApplyGenericOneQubitGate(Types::qubit_t qubit, const Eigen::Matrix2cd &gate) override
860 {
861 if (!mpo->ApplyOneQubitMatrix(static_cast<int>(qubit), reinterpret_cast<const double *>(gate.data())))
862 throw std::runtime_error("GpuSimulator::ApplyGenericOneQubitGate: Failed to apply the "
863 "generic one-qubit gate on the matrix product operator "
864 "simulator.");
865 NotifyObservers({qubit});
866 return;
867 }
868
869 void ApplyGenericTwoQubitGate(Types::qubit_t qubit0, Types::qubit_t qubit1, const Eigen::Matrix4cd &gate) override
870 {
871 if (!mpo->ApplyTwoQubitMatrix(static_cast<int>(qubit0), static_cast<int>(qubit1), reinterpret_cast<const double *>(gate.data())))
872 throw std::runtime_error("GpuSimulator::ApplyGenericTwoQubitGate: Failed to apply the "
873 "generic two-qubit gate on the matrix product operator "
874 "simulator.");
875 RecordExecutedTwoQubitGate(qubit0, qubit1, swapGatesRelabelQubits && IsExactSwapMatrix(gate), EffectiveSchmidtRank(gate));
876 NotifyObservers({qubit0, qubit1});
877 return;
878 }
879
880 void ApplyP(Types::qubit_t qubit, double lambda) override
881 {
882 mpo->ApplyP(qubit, lambda);
883 NotifyObservers({qubit});
884 }
885
886 void ApplyX(Types::qubit_t qubit) override
887 {
888 mpo->ApplyX(qubit);
889 NotifyObservers({qubit});
890 }
891
892 void ApplyY(Types::qubit_t qubit) override
893 {
894 mpo->ApplyY(qubit);
895 NotifyObservers({qubit});
896 }
897
898 void ApplyZ(Types::qubit_t qubit) override
899 {
900 mpo->ApplyZ(qubit);
901 NotifyObservers({qubit});
902 }
903
904 void ApplyH(Types::qubit_t qubit) override
905 {
906 mpo->ApplyH(qubit);
907 NotifyObservers({qubit});
908 }
909
910 void ApplyS(Types::qubit_t qubit) override
911 {
912 mpo->ApplyS(qubit);
913 NotifyObservers({qubit});
914 }
915
916 void ApplySDG(Types::qubit_t qubit) override
917 {
918 mpo->ApplySDG(qubit);
919 NotifyObservers({qubit});
920 }
921
922 void ApplyT(Types::qubit_t qubit) override
923 {
924 mpo->ApplyT(qubit);
925 NotifyObservers({qubit});
926 }
927
928 void ApplyTDG(Types::qubit_t qubit) override
929 {
930 mpo->ApplyTDG(qubit);
931 NotifyObservers({qubit});
932 }
933
934 void ApplySx(Types::qubit_t qubit) override
935 {
936 mpo->ApplySX(qubit);
937 NotifyObservers({qubit});
938 }
939
940 void ApplySxDAG(Types::qubit_t qubit) override
941 {
942 mpo->ApplySXDG(qubit);
943 NotifyObservers({qubit});
944 }
945
946 void ApplyK(Types::qubit_t qubit) override
947 {
948 mpo->ApplyK(qubit);
949 NotifyObservers({qubit});
950 }
951
952 void ApplyRx(Types::qubit_t qubit, double theta) override
953 {
954 mpo->ApplyRx(qubit, theta);
955 NotifyObservers({qubit});
956 }
957
958 void ApplyRy(Types::qubit_t qubit, double theta) override
959 {
960 mpo->ApplyRy(qubit, theta);
961 NotifyObservers({qubit});
962 }
963
964 void ApplyRz(Types::qubit_t qubit, double theta) override
965 {
966 mpo->ApplyRz(qubit, theta);
967 NotifyObservers({qubit});
968 }
969
970 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda, double gamma) override
971 {
972 mpo->ApplyU(qubit, theta, phi, lambda, gamma);
973 NotifyObservers({qubit});
974 }
975
976 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
977 {
978 mpo->ApplyCX(ctrl_qubit, tgt_qubit);
979 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
980 NotifyObservers({tgt_qubit, ctrl_qubit});
981 }
982
983 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
984 {
985 mpo->ApplyCY(ctrl_qubit, tgt_qubit);
986 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
987 NotifyObservers({tgt_qubit, ctrl_qubit});
988 }
989
990 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
991 {
992 mpo->ApplyCZ(ctrl_qubit, tgt_qubit);
993 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
994 NotifyObservers({tgt_qubit, ctrl_qubit});
995 }
996
997 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double lambda) override
998 {
999 mpo->ApplyCP(ctrl_qubit, tgt_qubit, lambda);
1000 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(lambda / 2.))));
1001 NotifyObservers({tgt_qubit, ctrl_qubit});
1002 }
1003
1004 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
1005 {
1006 mpo->ApplyCRx(ctrl_qubit, tgt_qubit, theta);
1007 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
1008 NotifyObservers({tgt_qubit, ctrl_qubit});
1009 }
1010
1011 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
1012 {
1013 mpo->ApplyCRy(ctrl_qubit, tgt_qubit, theta);
1014 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
1015 NotifyObservers({tgt_qubit, ctrl_qubit});
1016 }
1017
1018 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
1019 {
1020 mpo->ApplyCRz(ctrl_qubit, tgt_qubit, theta);
1021 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
1022 NotifyObservers({tgt_qubit, ctrl_qubit});
1023 }
1024
1025 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
1026 {
1027 mpo->ApplyCH(ctrl_qubit, tgt_qubit);
1028 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
1029 NotifyObservers({tgt_qubit, ctrl_qubit});
1030 }
1031
1032 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
1033 {
1034 mpo->ApplyCSX(ctrl_qubit, tgt_qubit);
1035 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(std::sqrt(2.)));
1036 NotifyObservers({tgt_qubit, ctrl_qubit});
1037 }
1038
1039 void ApplyCSxDAG(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
1040 {
1041 mpo->ApplyCSXDG(ctrl_qubit, tgt_qubit);
1042 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(std::sqrt(2.)));
1043 NotifyObservers({tgt_qubit, ctrl_qubit});
1044 }
1045
1046 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override
1047 {
1048 mpo->ApplySwap(qubit0, qubit1);
1049 RecordExecutedTwoQubitGate(qubit0, qubit1, true, 4.);
1050 NotifyObservers({qubit1, qubit0});
1051 }
1052
1053 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1, Types::qubit_t qubit2) override
1054 {
1055 {
1056 DecompositionScope scope(*this);
1057 const size_t q1 = qubit0;
1058 const size_t q2 = qubit1;
1059 const size_t q3 = qubit2;
1060 mpo->ApplyCSX(static_cast<int>(q2), static_cast<int>(q3));
1061 RecordExecutedTwoQubitGate(qubit1, qubit2, false, 2.);
1062 NotifyObservers({qubit1, qubit2});
1063 mpo->ApplyCX(static_cast<int>(q1), static_cast<int>(q2));
1064 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
1065 NotifyObservers({qubit0, qubit1});
1066 mpo->ApplyCSXDG(static_cast<int>(q2), static_cast<int>(q3));
1067 RecordExecutedTwoQubitGate(qubit1, qubit2, false, 2.);
1068 NotifyObservers({qubit1, qubit2});
1069 mpo->ApplyCX(static_cast<int>(q1), static_cast<int>(q2));
1070 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
1071 NotifyObservers({qubit0, qubit1});
1072 mpo->ApplyCSX(static_cast<int>(q1), static_cast<int>(q3));
1073 RecordExecutedTwoQubitGate(qubit0, qubit2, false, 2.);
1074 NotifyObservers({qubit0, qubit2});
1075 }
1076 NotifyObservers({qubit2, qubit1, qubit0});
1077 }
1078
1079 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0, Types::qubit_t qubit1) override
1080 {
1081 {
1082 DecompositionScope scope(*this);
1083 const size_t q1 = ctrl_qubit;
1084 const size_t q2 = qubit0;
1085 const size_t q3 = qubit1;
1086 mpo->ApplyCX(static_cast<int>(q3), static_cast<int>(q2));
1087 RecordExecutedTwoQubitGate(qubit1, qubit0, false, 2.);
1088 NotifyObservers({qubit1, qubit0});
1089 mpo->ApplyCSX(static_cast<int>(q2), static_cast<int>(q3));
1090 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
1091 NotifyObservers({qubit0, qubit1});
1092 mpo->ApplyCX(static_cast<int>(q1), static_cast<int>(q2));
1093 RecordExecutedTwoQubitGate(ctrl_qubit, qubit0, false, 2.);
1094 NotifyObservers({ctrl_qubit, qubit0});
1095 mpo->ApplyP(static_cast<int>(q3), M_PI);
1096 NotifyObservers({qubit1});
1097 mpo->ApplyP(static_cast<int>(q2), -M_PI_2);
1098 NotifyObservers({qubit0});
1099 mpo->ApplyCSX(static_cast<int>(q2), static_cast<int>(q3));
1100 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
1101 NotifyObservers({qubit0, qubit1});
1102 mpo->ApplyCX(static_cast<int>(q1), static_cast<int>(q2));
1103 RecordExecutedTwoQubitGate(ctrl_qubit, qubit0, false, 2.);
1104 NotifyObservers({ctrl_qubit, qubit0});
1105 mpo->ApplyP(static_cast<int>(q3), M_PI);
1106 NotifyObservers({qubit1});
1107 mpo->ApplyCSX(static_cast<int>(q1), static_cast<int>(q3));
1108 RecordExecutedTwoQubitGate(ctrl_qubit, qubit1, false, 2.);
1109 NotifyObservers({ctrl_qubit, qubit1});
1110 mpo->ApplyCX(static_cast<int>(q3), static_cast<int>(q2));
1111 RecordExecutedTwoQubitGate(qubit1, qubit0, false, 2.);
1112 NotifyObservers({qubit1, qubit0});
1113 }
1114 NotifyObservers({qubit1, qubit0, ctrl_qubit});
1115 }
1116
1117 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta, double phi, double lambda, double gamma) override
1118 {
1119 mpo->ApplyCU(ctrl_qubit, tgt_qubit, theta, phi, lambda, gamma);
1120 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.) * std::cos((phi + lambda) / 2.))));
1121 NotifyObservers({tgt_qubit, ctrl_qubit});
1122 }
1123
1124 std::unique_ptr<ISimulator> Clone() override
1125 {
1126 auto cloned = std::make_unique<GpuMPOSimulator>();
1127 cloned->swapGatesRelabelQubits = swapGatesRelabelQubits;
1128 cloned->nrQubits = nrQubits;
1129 cloned->lookaheadDepth = lookaheadDepth;
1130 cloned->lookaheadDepthWithHeuristic = lookaheadDepthWithHeuristic;
1131 cloned->useOptimalMeetingPosition = useOptimalMeetingPosition;
1132 cloned->upcomingGates = upcomingGates;
1133 cloned->upcomingGateIndex = upcomingGateIndex;
1134 cloned->growthFactorGate = growthFactorGate;
1135 cloned->growthFactorSwap = growthFactorSwap;
1136 cloned->configuration = configuration;
1137 if (mpo)
1138 {
1139 cloned->mpo = mpo->Clone();
1140 if (!cloned->mpo)
1141 throw std::runtime_error("GpuSimulator::Clone: Failed to clone matrix product operator "
1142 "state.");
1143 cloned->gateCounterObserver = std::make_shared<GateCounterObserver>(cloned->upcomingGateIndex);
1144 cloned->RegisterObserver(cloned->gateCounterObserver);
1145 cloned->curMaxBondDim = curMaxBondDim;
1146 cloned->mpo->SetCallbackContext(static_cast<GpuTensorChainSimulator *>(cloned.get()));
1147 }
1148 cloned->RefreshRoutingSettings();
1149 cloned->dummySim = dummySim ? dummySim->Clone() : nullptr;
1150 cloned->dummyHistoryValid = dummyHistoryValid;
1151 cloned->savedDummyBondDims = savedDummyBondDims;
1152 cloned->RefreshRoutingCallback();
1153 if (configuration.IsSet("seed"))
1154 cloned->SetSeed(DeriveSeed(std::stoull(configuration.GetConfiguration("seed")), nextSeedStream++));
1155 return cloned;
1156 }
1157
1158 protected:
1159 std::vector<double> CurrentBondDimensions() const override
1160 {
1161 if (!mpo)
1162 return {};
1163 const auto dims = mpo->GetBondDimensions(nrQubits);
1164 return {dims.begin(), dims.end()};
1165 }
1166
1167 std::vector<long long> CurrentRoutingMap() const override
1168 {
1169 if (mpo)
1170 return mpo->GetQubitsMap();
1171 return {};
1172 }
1173
1174 void RefreshRoutingCallback() override
1175 {
1176 const bool active = IsRoutingLookaheadEnabled() && !upcomingGates.empty();
1177 if (mpo)
1178 mpo->SetMeetingPositionCallback(active ? GetMeetingPositionCallback() : nullptr);
1179 }
1180
1181 std::unique_ptr<GpuMPO> mpo;
1182};
1183} // namespace Simulators::Private
1184#endif
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyCSwap(void *sim, int controlQubit, int qubit1, int qubit2)
int ApplyCCX(void *sim, int controlQubit1, int controlQubit2, int targetQubit)
int ApplyY(void *sim, int qubit)
double * Amplitude(void *sim, unsigned long long int outcome)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int 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 ApplyGenericOneQubitGate(void *sim, unsigned long q0, const double *buffer)
int ApplyGenericTwoQubitGate(void *sim, unsigned long q0, unsigned long q1, const double *buffer)
int SaveState(void *sim)
SimulationType
The type of simulation.
Definition State.h:109
double EffectiveSchmidtRank(const Eigen::MatrixXcd &u)
double ControlledSchmidtRank(double traceAbs)
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