Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
GpuMPSSimulator.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_state backend. Owns exactly one native Gpu implementation.
9class GpuMPSSimulator : public GpuTensorChainSimulator
10{
11 public:
12 GpuMPSSimulator() : GpuTensorChainSimulator(false)
13 {
14 configuration.SetConfiguration("method", "matrix_product_state");
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_state";
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()->MPSSwapRelabels());
37 {
38 mps = SimulatorsFactory::CreateGpuLibMPSSim(gpuDevice);
39 if (mps)
40 {
41 mps->SetCallbackContext(static_cast<GpuTensorChainSimulator *>(this));
42 curMaxBondDim = 1;
43 mps->InstallBondSummary(&GpuTensorChainSimulator::BondSummaryCallback, &GpuTensorChainSimulator::BondDimCallback);
44 for (const auto &[key, value] : configuration.GetConfigMap())
45 if (key != "method")
46 Configure(key.c_str(), value.c_str());
47 const bool res = mps->Create(nrQubits);
48 if (!res)
49 throw std::runtime_error("GpuState::Initialize: Failed to create "
50 "and initialize the MPS state.");
51 }
52 else
53 throw std::runtime_error("GpuState::Initialize: Failed to create the MPS state.");
54 if (!useOptimalMeetingPosition)
55 mps->SetUseOptimalMeetingPosition(false);
56 }
57 if (GetGpuDevice() != gpuDevice)
58 throw std::runtime_error("GpuState::Initialize: GPU plugin did not confirm the requested "
59 "device; update the GPU library");
60 }
61 }
62
63 void InitializeToBasisState(size_t num_qubits, Types::qubit_t basisState) override
64 {
65 if (num_qubits == 0)
66 return;
67 Clear();
68 nrQubits = num_qubits;
69 Initialize();
70 const bool created = mps->CreateWithBasisState(nrQubits, static_cast<unsigned long long>(basisState));
71 if (!created)
72 throw std::runtime_error("GpuState::InitializeToBasisState: Failed to initialize the "
73 "state.");
74 }
75
76 void InitializeToBasisState(size_t num_qubits, const std::vector<bool> &basisState) override
77 {
78 if (num_qubits == 0)
79 return;
80 Clear();
81 nrQubits = num_qubits;
82 Initialize();
83 bool created = true;
84 {
85 std::vector<unsigned char> stateBits(num_qubits, 0);
86 for (size_t q = 0; q < num_qubits && q < basisState.size(); ++q)
87 stateBits[q] = basisState[q] ? 1 : 0;
88 created = mps->CreateWithBasisStateBits(nrQubits, stateBits);
89 }
90 if (!created)
91 throw std::runtime_error("GpuState::InitializeToBasisState: Failed to initialize the "
92 "state.");
93 }
94
95 void Reset() override
96 {
97 if (mps)
98 {
99 mps->Reset();
100 curMaxBondDim = 1;
101 }
102 upcomingGateIndex = 0;
103 ResetDummySimulator();
104 }
105
106 void SetInitialQubitsMap(const std::vector<long long int> &initialMap) override
107 {
108 if (mps)
109 {
110 mps->SetInitialQubitsMap(initialMap);
111 EnsureDummySimulator(initialMap.size());
112 dummySim->SetInitialQubitsMap(initialMap);
113 }
114 }
115
116 void SetUseOptimalMeetingPosition(bool enable) override
117 {
118 useOptimalMeetingPosition = enable;
119 RefreshRoutingSettings();
120 if (mps)
121 mps->SetUseOptimalMeetingPosition(enable);
122 RefreshRoutingCallback();
123 }
124
125 void Configure(const char *key, const char *value) override
126 {
127 if (!key || !value)
128 return;
129 if (std::string("gpu_device") == key)
130 {
131 const int device = Configuration::ParseGpuDevice(value);
132 if ((mps) && device != Configuration::ParseGpuDevice(configuration.GetConfiguration(key)))
133 throw std::invalid_argument("gpu_device cannot change after initialization; clear the "
134 "simulator first");
135 configuration.SetConfiguration(key, std::to_string(device));
136 return;
137 }
138 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
139 if (!svdGroup.empty())
140 {
141 const bool enabled = Configuration::ParseGpuSvdFlag(value);
142 const bool gesvd = svdGroup == key;
143 const char algorithm = std::string(key).back();
144 const auto apply = [algorithm, enabled](auto &backend) {
145 if (!backend)
146 return true;
147 if (algorithm == 'j')
148 return backend->SetGesvdJ(enabled);
149 if (algorithm == 'p')
150 return backend->SetGesvdP(enabled);
151 return backend->SetGesvdR(enabled);
152 };
153 const auto applyGesvd = [enabled](auto &backend) {
154 if (!enabled || !backend)
155 return true;
156 return backend->SetGesvdJ(false) && backend->SetGesvdP(false) && backend->SetGesvdR(false);
157 };
158 bool applied = true;
159 if (svdGroup == "matrix_product_state_use_gesvd")
160 applied = gesvd ? applyGesvd(mps) : apply(mps);
161 if (!applied)
162 throw std::runtime_error(std::string("GPU library cannot apply ") + key + "; an updated GPU plugin may be required");
163 configuration.SetConfiguration(key, value);
164 return;
165 }
166 if (std::string("method") == key)
167 {
168 ValidateMethod(value);
169 configuration.SetConfiguration(key, value);
170 return;
171 }
172 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
173 {
174 // Precision is immutable after native storage is allocated.
175 // Replayed settings take effect after Clear and Initialize.
176 const bool useDouble = Configuration::ParsePrecision(key, value);
177 if ((mps && mps->IsCreated()))
178 return;
179 if ((mps && !mps->SetDataType(useDouble)))
180 throw std::runtime_error("GPU precision configuration failed");
181 configuration.SetConfiguration(key, value);
182 return;
183 }
184 if (!configuration.WasApplied(key, value))
185 configuration.SetConfiguration(key, value);
186 RefreshRoutingSettings();
187 if (std::string("seed") == key)
188 {
189 const uint64_t seed = std::stoull(value);
190 SeedAuxiliaryRng(seed);
191 nextSeedStream = 0;
192 if (mps)
193 mps->SetSeed(seed);
194 return;
195 }
196 if (std::string("matrix_product_state_truncation_threshold") == key || std::string("matrix_product_operator_truncation_threshold") == key)
197 {
198 const double singularValueThreshold = std::stod(value);
199 if (singularValueThreshold >= 0.)
200 {
201 if (mps)
202 mps->SetCutoff(singularValueThreshold);
203 }
204 }
205 else if (std::string("matrix_product_state_truncation_mode") == key || std::string("matrix_product_operator_truncation_mode") == key)
206 {
207 int truncationMode = -1;
208 if (std::string("relative_max") == value)
209 truncationMode = 0;
210 else if (std::string("discarded_weight") == value)
211 truncationMode = 1;
212 if (truncationMode >= 0)
213 {
214 if (mps)
215 mps->SetTruncationMode(truncationMode);
216 }
217 }
218 else if (std::string("matrix_product_state_max_bond_dimension") == key || std::string("matrix_product_operator_max_bond_dimension") == key)
219 {
220 const long long int chi = std::stoi(value);
221 if (chi > 0)
222 {
223 const std::string bondDimension(value);
224 for (const char *alias : {"matrix_product_state_max_bond_dimension", "matrix_product_operator_max_bond_dimension"})
225 if (!configuration.WasApplied(alias, bondDimension))
226 configuration.SetConfiguration(alias, bondDimension);
227 RefreshRoutingSettings();
228 if (mps)
229 mps->SetMaxExtent(chi);
230 if (dummySim)
231 dummySim->SetMaxBondDimension(EffectiveLookaheadMaxBondDimension(dummySim->getNrQubits(), chi));
232 }
233 }
234 }
235
236 std::string GetConfiguration(const char *key) const override
237 {
238 if (!key)
239 return {};
240 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
241 {
242 const bool useDouble = mps ? mps->IsDoublePrecision() : configuration.GetConfiguration("precision") == "double";
243 return std::string(key) == "precision" ? (useDouble ? "double" : "single") : (useDouble ? "1" : "0");
244 }
245 if (!key)
246 return {};
247 const auto svdGroup = Configuration::GpuSvdSettingGroup(key);
248 if (!svdGroup.empty())
249 {
250 if (svdGroup == key)
251 {
252 const auto readGesvd = [](const auto &backend) { return !backend->GetGesvdJ() && !backend->GetGesvdP() && !backend->GetGesvdR(); };
253 if (svdGroup == "matrix_product_state_use_gesvd" && mps)
254 return readGesvd(mps) ? "true" : "false";
255 }
256 const char algorithm = std::string(key).back();
257 const auto read = [algorithm](const auto &backend) {
258 if (algorithm == 'j')
259 return backend->GetGesvdJ();
260 if (algorithm == 'p')
261 return backend->GetGesvdP();
262 return backend->GetGesvdR();
263 };
264 if (svdGroup == "matrix_product_state_use_gesvd" && mps)
265 return read(mps) ? "true" : "false";
266 }
267 if (std::string("method") == key)
268 return MethodName();
269 return configuration.GetConfiguration(key);
270 }
271
272 size_t AllocateQubits(size_t num_qubits) override
273 {
274 if (mps)
275 return 0;
276 const size_t oldNrQubits = nrQubits;
277 nrQubits += num_qubits;
278 return oldNrQubits;
279 }
280
281 void Clear() override
282 {
283 mps = nullptr;
284 nrQubits = 0;
285 ClearDummySimulator();
286 upcomingGateIndex = 0;
287 upcomingGates.clear();
288 }
289
290 size_t Measure(const Types::qubits_vector &qubits) override
291 {
292 InvalidateDummyHistory();
293 if (qubits.size() > sizeof(size_t) * 8)
294 std::cerr << "Warning: The number of qubits to measure is larger than the "
295 "number of bits in the size_t type, the outcome will be undefined"
296 << std::endl;
297 size_t res = 0;
298 size_t mask = 1ULL;
299 DontNotify();
300 {
301 for (size_t qubit : qubits)
302 {
303 if (mps->Measure(static_cast<unsigned int>(qubit)))
304 res |= mask;
305 mask <<= 1;
306 }
307 }
308 Notify();
309 NotifyObservers(qubits);
310 return res;
311 }
312
313 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override
314 {
315 InvalidateDummyHistory();
316 std::vector<bool> res(qubits.size(), false);
317 DontNotify();
318 {
319 for (size_t i = 0; i < qubits.size(); ++i)
320 res[i] = mps->Measure(static_cast<unsigned int>(qubits[i]));
321 }
322 Notify();
323 NotifyObservers(qubits);
324 return res;
325 }
326
327 void ApplyReset(const Types::qubits_vector &qubits) override
328 {
329 InvalidateDummyHistory();
330 DontNotify();
331 {
332 for (size_t qubit : qubits)
333 if (mps->Measure(static_cast<unsigned int>(qubit)))
334 mps->ApplyX(static_cast<unsigned int>(qubit));
335 }
336 Notify();
337 NotifyObservers(qubits);
338 }
339
340 std::complex<double> ExpectationValueOperators(const Types::qubits_vector &qubits, const std::vector<Eigen::MatrixXcd> &matrices) override
341 {
342 if (!mps)
343 return IState::ExpectationValueOperators(qubits, matrices);
344 TensorQueries::Operators(qubits, matrices, nrQubits);
345 std::vector<int> targets;
346 std::vector<double> raw;
347 targets.reserve(qubits.size());
348 raw.reserve(8 * matrices.size());
349 for (size_t i = 0; i < qubits.size(); ++i)
350 {
351 targets.push_back(TensorQueries::Count(qubits[i]));
352 for (Eigen::Index e = 0; e < 4; ++e)
353 {
354 raw.push_back(matrices[i].data()[e].real());
355 raw.push_back(matrices[i].data()[e].imag());
356 }
357 }
358 return mps->ExpectationValueOperators(targets, raw);
359 }
360
361 void MoveAtBeginningOfChain(const Types::qubits_vector &qubits) override
362 {
363 if (!mps)
364 return IState::MoveAtBeginningOfChain(qubits);
365 const auto selected = MPOValidation::Qubits(qubits, nrQubits);
366 if (selected.empty())
367 return;
368 mps->MoveAtBeginningOfChain(selected);
369 InvalidateDummyHistory();
370 }
371
372 std::vector<std::complex<double>> GetStateVector() override
373 {
374 if (!mps)
375 return IState::GetStateVector();
376 return mps->GetStateVector();
377 }
378
379 void Trim() override
380 {
381 InvalidateDummyHistory();
382 if (mps)
383 {
384 if (!mps->Trim())
385 throw std::runtime_error("GPU MPS trim failed");
386 }
387 else
388 throw std::runtime_error("Trim is only available for GPU MPS and MPO");
389 }
390
391 void ReCanonicalize() override
392 {
393 InvalidateDummyHistory();
394 if (mps)
395 {
396 if (!mps->ReCanonicalize())
397 throw std::runtime_error("GPU MPS canonicalization failed");
398 }
399 else
400 throw std::runtime_error("Canonicalization is only available for GPU MPS and MPO");
401 }
402
403 std::complex<double> Amplitude(Types::qubit_t outcome) override
404 {
405 double real = 0.0;
406 double imag = 0.0;
407 {
408 std::vector<long int> fixedValues(nrQubits);
409 for (size_t i = 0; i < nrQubits; ++i)
410 fixedValues[i] = i < std::numeric_limits<Types::qubit_t>::digits ? (outcome >> i) & 1 : 0;
411 {
412 if (!mps->Amplitude(nrQubits, fixedValues.data(), &real, &imag) || !std::isfinite(real) || !std::isfinite(imag))
413 throw std::runtime_error("GPU MPS amplitude query failed");
414 }
415 }
416 return std::complex<double>(real, imag);
417 }
418
419 std::complex<double> ProjectOnZero() override
420 {
421 return mps->ProjectOnZero();
422 }
423
424 std::vector<double> AllProbabilities() override
425 {
426 if (nrQubits == 0)
427 return {};
428 if (mps)
429 return mps->AllProbabilities();
430 if (nrQubits >= std::numeric_limits<size_t>::digits)
431 throw std::length_error("Full probability enumeration exceeds the basis-index API; query "
432 "selected bit vectors");
433 const size_t numStates = size_t{1} << nrQubits;
434 std::vector<double> result(numStates);
435 {
436 for (Types::qubit_t i = 0; i < (Types::qubit_t)numStates; ++i)
437 {
438 const auto val = Amplitude(i);
439 result[i] = std::norm(std::complex<double>(val.real(), val.imag()));
440 }
441 }
442 return result;
443 }
444
445 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(const Types::qubits_vector &qubits, size_t shots = 1000) override
446 {
447 if (mps)
448 {
449 if (qubits.size() > sizeof(Types::qubit_t) * 8)
450 throw std::invalid_argument("Use SampleCountsMany for more than 64 measured qubits");
451 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
452 for (const auto &entry : SampleCountsMany(qubits, shots))
453 {
454 Types::qubit_t value = 0;
455 for (size_t i = 0; i < entry.first.size(); ++i)
456 if (entry.first[i])
457 value |= Types::qubit_t{1} << i;
458 result[value] += entry.second;
459 }
460 return result;
461 }
462 if (qubits.empty() || shots == 0)
463 return {};
464 if (qubits.size() > sizeof(Types::qubit_t) * 8)
465 std::cerr << "Warning: The number of qubits to measure is larger than the "
466 "number of bits in the Types::qubit_t type, the outcome will be "
467 "undefined"
468 << std::endl;
469 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
470 DontNotify();
471 Notify();
472 NotifyObservers(qubits);
473 return result;
474 }
475
476 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(const Types::qubits_vector &qubits, size_t shots = 1000) override
477 {
478 if (mps)
479 {
480 std::vector<unsigned int> selected;
481 for (auto q : qubits)
482 {
483 if (q >= nrQubits || q > static_cast<size_t>(std::numeric_limits<int>::max()))
484 throw std::out_of_range("GPU tensor sampled qubit is out of range");
485 selected.push_back(static_cast<unsigned int>(q));
486 }
487 if (qubits.empty() || shots == 0)
488 return {};
489 std::sort(selected.begin(), selected.end());
490 selected.erase(std::unique(selected.begin(), selected.end()), selected.end());
491 const auto counts = mps->SampleHistogram(shots, std::move(selected));
492 // Current plugins sample without moving sites. Check the map for
493 // plugins that implement selected sampling by reordering the chain.
494 ValidateDummyHistoryMap();
495 const auto positions = SampleBitPositions(qubits);
496 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
497 for (const auto &entry : counts)
498 {
499 std::vector<bool> bits(qubits.size());
500 for (size_t i = 0; i < bits.size(); ++i)
501 bits[i] = entry.first[positions[i]];
502 result[std::move(bits)] += entry.second;
503 }
504 NotifyObservers(qubits);
505 return result;
506 }
507 if (qubits.empty() || shots == 0)
508 return {};
509 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
510 DontNotify();
511 Notify();
512 NotifyObservers(qubits);
513 return result;
514 }
515
516 double ExpectationValue(const std::string &pauliString) override
517 {
518 double result = 0.0;
519 result = mps->ExpectationValue(pauliString);
520 return result;
521 }
522
523 std::vector<double> ExpectationValues(const std::vector<std::string> &paulis) override
524 {
525 if (!mps)
526 return IState::ExpectationValues(paulis);
527 std::vector<double> result(paulis.size(), 0.);
528 std::vector<std::string> selected;
529 std::vector<size_t> indices;
530 for (size_t i = 0; i < paulis.size(); ++i)
531 {
532 if (paulis[i].empty())
533 {
534 result[i] = 1.;
535 continue;
536 }
537 if (paulis[i].size() > nrQubits)
538 continue;
539 auto pauli = paulis[i];
540 for (char &p : pauli)
541 p = static_cast<char>(std::toupper(static_cast<unsigned char>(p)));
542 if (pauli.find_first_not_of("IXYZ") != std::string::npos)
543 continue;
544 indices.push_back(i);
545 selected.push_back(std::move(pauli));
546 }
547 if (selected.empty())
548 return result;
549 const auto values = mps->ExpectationValues(selected);
550 for (size_t i = 0; i < indices.size(); ++i)
551 result[indices[i]] = values[i];
552 return result;
553 }
554
555 int GetGpuDevice() const override
556 {
557 if (mps)
558 return mps->GetGpuDevice();
559 return -1;
560 }
561
562 SimulationType GetSimulationType() const override
563 {
564 return SimulationType::kMatrixProductState;
565 }
566
567 void SaveState() override
568 {
569 if (!mps->SaveState())
570 throw std::runtime_error("GPU MPS state save failed");
571 SaveDummyState();
572 }
573
574 void RestoreState() override
575 {
576 if (!mps->RestoreState())
577 throw std::runtime_error("GPU MPS state restore failed");
578 RestoreDummyState();
579 }
580
581 void ApplyGenericOneQubitGate(Types::qubit_t qubit, const Eigen::Matrix2cd &gate) override
582 {
583 const Eigen::Matrix2cd inputFirst = gate.transpose();
584 if (!mps->ApplyOneQubitMatrix(static_cast<unsigned int>(qubit), reinterpret_cast<const double *>(inputFirst.data())))
585 throw std::runtime_error("GpuSimulator::ApplyGenericOneQubitGate: Failed to apply the "
586 "generic one-qubit gate on the matrix product state simulator.");
587 NotifyObservers({qubit});
588 return;
589 }
590
591 void ApplyGenericTwoQubitGate(Types::qubit_t qubit0, Types::qubit_t qubit1, const Eigen::Matrix4cd &gate) override
592 {
593 const Eigen::Matrix4cd inputFirst = gate.transpose();
594 if (!mps->ApplyTwoQubitMatrix(static_cast<unsigned int>(qubit0), static_cast<unsigned int>(qubit1),
595 reinterpret_cast<const double *>(inputFirst.data())))
596 throw std::runtime_error("GpuSimulator::ApplyGenericTwoQubitGate: Failed to apply the "
597 "generic two-qubit gate on the matrix product state simulator.");
598 RecordExecutedTwoQubitGate(qubit0, qubit1, swapGatesRelabelQubits && IsExactSwapMatrix(gate), EffectiveSchmidtRank(gate));
599 NotifyObservers({qubit0, qubit1});
600 return;
601 }
602
603 void ApplyP(Types::qubit_t qubit, double lambda) override
604 {
605 mps->ApplyP(qubit, lambda);
606 NotifyObservers({qubit});
607 }
608
609 void ApplyX(Types::qubit_t qubit) override
610 {
611 mps->ApplyX(qubit);
612 NotifyObservers({qubit});
613 }
614
615 void ApplyY(Types::qubit_t qubit) override
616 {
617 mps->ApplyY(qubit);
618 NotifyObservers({qubit});
619 }
620
621 void ApplyZ(Types::qubit_t qubit) override
622 {
623 mps->ApplyZ(qubit);
624 NotifyObservers({qubit});
625 }
626
627 void ApplyH(Types::qubit_t qubit) override
628 {
629 mps->ApplyH(qubit);
630 NotifyObservers({qubit});
631 }
632
633 void ApplyS(Types::qubit_t qubit) override
634 {
635 mps->ApplyS(qubit);
636 NotifyObservers({qubit});
637 }
638
639 void ApplySDG(Types::qubit_t qubit) override
640 {
641 mps->ApplySDG(qubit);
642 NotifyObservers({qubit});
643 }
644
645 void ApplyT(Types::qubit_t qubit) override
646 {
647 mps->ApplyT(qubit);
648 NotifyObservers({qubit});
649 }
650
651 void ApplyTDG(Types::qubit_t qubit) override
652 {
653 mps->ApplyTDG(qubit);
654 NotifyObservers({qubit});
655 }
656
657 void ApplySx(Types::qubit_t qubit) override
658 {
659 mps->ApplySX(qubit);
660 NotifyObservers({qubit});
661 }
662
663 void ApplySxDAG(Types::qubit_t qubit) override
664 {
665 mps->ApplySXDG(qubit);
666 NotifyObservers({qubit});
667 }
668
669 void ApplyK(Types::qubit_t qubit) override
670 {
671 mps->ApplyK(qubit);
672 NotifyObservers({qubit});
673 }
674
675 void ApplyRx(Types::qubit_t qubit, double theta) override
676 {
677 mps->ApplyRx(qubit, theta);
678 NotifyObservers({qubit});
679 }
680
681 void ApplyRy(Types::qubit_t qubit, double theta) override
682 {
683 mps->ApplyRy(qubit, theta);
684 NotifyObservers({qubit});
685 }
686
687 void ApplyRz(Types::qubit_t qubit, double theta) override
688 {
689 mps->ApplyRz(qubit, theta);
690 NotifyObservers({qubit});
691 }
692
693 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda, double gamma) override
694 {
695 mps->ApplyU(qubit, theta, phi, lambda, gamma);
696 NotifyObservers({qubit});
697 }
698
699 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
700 {
701 mps->ApplyCX(ctrl_qubit, tgt_qubit);
702 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
703 NotifyObservers({tgt_qubit, ctrl_qubit});
704 }
705
706 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
707 {
708 mps->ApplyCY(ctrl_qubit, tgt_qubit);
709 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
710 NotifyObservers({tgt_qubit, ctrl_qubit});
711 }
712
713 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
714 {
715 mps->ApplyCZ(ctrl_qubit, tgt_qubit);
716 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
717 NotifyObservers({tgt_qubit, ctrl_qubit});
718 }
719
720 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double lambda) override
721 {
722 mps->ApplyCP(ctrl_qubit, tgt_qubit, lambda);
723 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(lambda / 2.))));
724 NotifyObservers({tgt_qubit, ctrl_qubit});
725 }
726
727 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
728 {
729 mps->ApplyCRx(ctrl_qubit, tgt_qubit, theta);
730 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
731 NotifyObservers({tgt_qubit, ctrl_qubit});
732 }
733
734 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
735 {
736 mps->ApplyCRy(ctrl_qubit, tgt_qubit, theta);
737 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
738 NotifyObservers({tgt_qubit, ctrl_qubit});
739 }
740
741 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
742 {
743 mps->ApplyCRz(ctrl_qubit, tgt_qubit, theta);
744 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.))));
745 NotifyObservers({tgt_qubit, ctrl_qubit});
746 }
747
748 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
749 {
750 mps->ApplyCH(ctrl_qubit, tgt_qubit);
751 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, 2.);
752 NotifyObservers({tgt_qubit, ctrl_qubit});
753 }
754
755 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
756 {
757 mps->ApplyCSX(ctrl_qubit, tgt_qubit);
758 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(std::sqrt(2.)));
759 NotifyObservers({tgt_qubit, ctrl_qubit});
760 }
761
762 void ApplyCSxDAG(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
763 {
764 mps->ApplyCSXDG(ctrl_qubit, tgt_qubit);
765 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(std::sqrt(2.)));
766 NotifyObservers({tgt_qubit, ctrl_qubit});
767 }
768
769 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override
770 {
771 if (!mps || !mps->ApplySwap(qubit0, qubit1))
772 throw std::runtime_error("GpuSimulator::ApplySwap: Failed to apply SWAP on the matrix product state simulator.");
773 RecordExecutedTwoQubitGate(qubit0, qubit1, true, 4.);
774 NotifyObservers({qubit1, qubit0});
775 }
776
777 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1, Types::qubit_t qubit2) override
778 {
779 {
780 DecompositionScope scope(*this);
781 const size_t q1 = qubit0;
782 const size_t q2 = qubit1;
783 const size_t q3 = qubit2;
784 mps->ApplyCSX(static_cast<unsigned int>(q2), static_cast<unsigned int>(q3));
785 RecordExecutedTwoQubitGate(qubit1, qubit2, false, 2.);
786 NotifyObservers({qubit1, qubit2});
787 mps->ApplyCX(static_cast<unsigned int>(q1), static_cast<unsigned int>(q2));
788 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
789 NotifyObservers({qubit0, qubit1});
790 mps->ApplyCSXDG(static_cast<unsigned int>(q2), static_cast<unsigned int>(q3));
791 RecordExecutedTwoQubitGate(qubit1, qubit2, false, 2.);
792 NotifyObservers({qubit1, qubit2});
793 mps->ApplyCX(static_cast<unsigned int>(q1), static_cast<unsigned int>(q2));
794 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
795 NotifyObservers({qubit0, qubit1});
796 mps->ApplyCSX(static_cast<unsigned int>(q1), static_cast<unsigned int>(q3));
797 RecordExecutedTwoQubitGate(qubit0, qubit2, false, 2.);
798 NotifyObservers({qubit0, qubit2});
799 }
800 NotifyObservers({qubit2, qubit1, qubit0});
801 }
802
803 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0, Types::qubit_t qubit1) override
804 {
805 {
806 DecompositionScope scope(*this);
807 const size_t q1 = ctrl_qubit;
808 const size_t q2 = qubit0;
809 const size_t q3 = qubit1;
810 mps->ApplyCX(static_cast<unsigned int>(q3), static_cast<unsigned int>(q2));
811 RecordExecutedTwoQubitGate(qubit1, qubit0, false, 2.);
812 NotifyObservers({qubit1, qubit0});
813 mps->ApplyCSX(static_cast<unsigned int>(q2), static_cast<unsigned int>(q3));
814 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
815 NotifyObservers({qubit0, qubit1});
816 mps->ApplyCX(static_cast<unsigned int>(q1), static_cast<unsigned int>(q2));
817 RecordExecutedTwoQubitGate(ctrl_qubit, qubit0, false, 2.);
818 NotifyObservers({ctrl_qubit, qubit0});
819 mps->ApplyP(static_cast<unsigned int>(q3), M_PI);
820 NotifyObservers({qubit1});
821 mps->ApplyP(static_cast<unsigned int>(q2), -M_PI_2);
822 NotifyObservers({qubit0});
823 mps->ApplyCSX(static_cast<unsigned int>(q2), static_cast<unsigned int>(q3));
824 RecordExecutedTwoQubitGate(qubit0, qubit1, false, 2.);
825 NotifyObservers({qubit0, qubit1});
826 mps->ApplyCX(static_cast<unsigned int>(q1), static_cast<unsigned int>(q2));
827 RecordExecutedTwoQubitGate(ctrl_qubit, qubit0, false, 2.);
828 NotifyObservers({ctrl_qubit, qubit0});
829 mps->ApplyP(static_cast<unsigned int>(q3), M_PI);
830 NotifyObservers({qubit1});
831 mps->ApplyCSX(static_cast<unsigned int>(q1), static_cast<unsigned int>(q3));
832 RecordExecutedTwoQubitGate(ctrl_qubit, qubit1, false, 2.);
833 NotifyObservers({ctrl_qubit, qubit1});
834 mps->ApplyCX(static_cast<unsigned int>(q3), static_cast<unsigned int>(q2));
835 RecordExecutedTwoQubitGate(qubit1, qubit0, false, 2.);
836 NotifyObservers({qubit1, qubit0});
837 }
838 NotifyObservers({qubit1, qubit0, ctrl_qubit});
839 }
840
841 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta, double phi, double lambda, double gamma) override
842 {
843 mps->ApplyCU(ctrl_qubit, tgt_qubit, theta, phi, lambda, gamma);
844 RecordExecutedTwoQubitGate(ctrl_qubit, tgt_qubit, false, ControlledSchmidtRank(2. * std::abs(std::cos(theta / 2.) * std::cos((phi + lambda) / 2.))));
845 NotifyObservers({tgt_qubit, ctrl_qubit});
846 }
847
848 std::unique_ptr<ISimulator> Clone() override
849 {
850 auto cloned = std::make_unique<GpuMPSSimulator>();
851 cloned->swapGatesRelabelQubits = swapGatesRelabelQubits;
852 cloned->nrQubits = nrQubits;
853 cloned->lookaheadDepth = lookaheadDepth;
854 cloned->lookaheadDepthWithHeuristic = lookaheadDepthWithHeuristic;
855 cloned->useOptimalMeetingPosition = useOptimalMeetingPosition;
856 cloned->upcomingGates = upcomingGates;
857 cloned->upcomingGateIndex = upcomingGateIndex;
858 cloned->growthFactorGate = growthFactorGate;
859 cloned->growthFactorSwap = growthFactorSwap;
860 cloned->configuration = configuration;
861 if (mps)
862 {
863 cloned->mps = mps->Clone();
864 cloned->gateCounterObserver = std::make_shared<GateCounterObserver>(cloned->upcomingGateIndex);
865 cloned->RegisterObserver(cloned->gateCounterObserver);
866 cloned->curMaxBondDim = curMaxBondDim;
867 cloned->mps->SetCallbackContext(static_cast<GpuTensorChainSimulator *>(cloned.get()));
868 }
869 cloned->RefreshRoutingSettings();
870 cloned->dummySim = dummySim ? dummySim->Clone() : nullptr;
871 cloned->dummyHistoryValid = dummyHistoryValid;
872 cloned->savedDummyBondDims = savedDummyBondDims;
873 cloned->RefreshRoutingCallback();
874 if (configuration.IsSet("seed"))
875 cloned->SetSeed(DeriveSeed(std::stoull(configuration.GetConfiguration("seed")), nextSeedStream++));
876 return cloned;
877 }
878
879 protected:
880 std::vector<double> CurrentBondDimensions() const override
881 {
882 if (!mps)
883 return {};
884 const auto dims = mps->GetBondDimensions(nrQubits);
885 return {dims.begin(), dims.end()};
886 }
887
888 std::vector<long long> CurrentRoutingMap() const override
889 {
890 if (mps)
891 return mps->GetQubitsMap();
892 return {};
893 }
894
895 void RefreshRoutingCallback() override
896 {
897 const bool active = IsRoutingLookaheadEnabled() && !upcomingGates.empty();
898 if (mps)
899 mps->SetMeetingPositionCallback(active ? GetMeetingPositionCallback() : nullptr);
900 }
901
902 std::unique_ptr<GpuLibMPSSim> mps;
903};
904} // namespace Simulators::Private
905#endif
int ApplyK(void *sim, int qubit)
char * GetConfiguration(void *sim, const char *key)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
double * AllProbabilities(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyCSwap(void *sim, int controlQubit, int qubit1, int qubit2)
int ApplyCCX(void *sim, int controlQubit1, int controlQubit2, int targetQubit)
int ApplyY(void *sim, int qubit)
double * Amplitude(void *sim, unsigned long long int outcome)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int ApplyCH(void *sim, int controlQubit, int targetQubit)
int GetSimulationType(void *sim)
unsigned long long int * SampleCounts(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits, unsigned long int shots)
int ApplyCZ(void *sim, int controlQubit, int targetQubit)
int ApplyRz(void *sim, int qubit, double theta)
int ApplyT(void *sim, int qubit)
int ApplyCRx(void *sim, int controlQubit, int targetQubit, double theta)
int 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