Maestro 0.2.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
SimpleDisconnectedNetwork.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _SIMPLE_NETWORK_H_
16#define _SIMPLE_NETWORK_H_
17
18#include "QubitRegister.h"
19#include "SimpleController.h"
20#include "SimpleHost.h"
22#include "NetworkJob.h"
23
25
26namespace Network {
27
41template <typename Time = Types::time_type,
42 class Controller = SimpleController<Time>>
43class SimpleDisconnectedNetwork : public INetwork<Time> {
44 public:
48
58 SimpleDisconnectedNetwork(const std::vector<Types::qubit_t> &qubits = {},
59 const std::vector<size_t> &cbits = {}) {
60 if (!qubits.empty()) CreateNetwork(qubits, cbits);
61 }
62
72 void CreateNetwork(const std::vector<Types::qubit_t> &qubits,
73 const std::vector<size_t> &cbits) {
74 size_t qubitsOffset = 0;
75 size_t cbitsOffset = 0;
76
77 for (size_t i = 0; i < qubits.size(); ++i) {
78 const size_t numQubits = qubits[i];
79 const size_t numBits = (i < cbits.size() ? cbits[i] : 0);
80 hosts.emplace_back(std::make_shared<SimpleHost<Time>>(
81 i, qubitsOffset, numQubits, cbitsOffset, numBits));
82 qubitsOffset += numQubits;
83 cbitsOffset += numBits;
84 }
85
86 for (size_t i = 0; i < hosts.size(); ++i) {
87 std::static_pointer_cast<SimpleHost<Time>>(hosts[i])->SetEntangledQubitId(
88 qubitsOffset);
89 std::static_pointer_cast<SimpleHost<Time>>(hosts[i])
90 ->SetEntangledQubitMeasurementBit(cbitsOffset);
91 ++qubitsOffset;
92 ++cbitsOffset;
93 }
94
95 controller = std::make_shared<Controller>();
96 }
97
109 const std::shared_ptr<Circuits::Circuit<Time>> &circuit) override {
110 const auto recreate = recreateIfNeeded;
111
114 size_t numQubits = 2;
115 if (simulator) {
116 simType = simulator->GetType();
117 method = simulator->GetSimulationType();
118 numQubits = simulator->GetNumberOfQubits();
119 }
120
121 recreateIfNeeded = false;
122
123 const auto res = RepeatedExecute(circuit, 1);
124
125 recreateIfNeeded = recreate;
126
127 // put the results in the state
128 if (!res.empty()) {
129 const auto &first = *res.begin();
130 GetState().SetResultsInOrder(first.first);
131 }
132
133 if (recreate &&
134 (!simulator ||
135 (simulator && (simType != simulator->GetType() ||
136 method != simulator->GetSimulationType() ||
137 simulator->GetNumberOfQubits() != numQubits))))
138 CreateSimulator(simType, method);
139 }
140
153 void ExecuteOnHost(const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
154 size_t hostId) override {
155 const auto recreate = recreateIfNeeded;
156
159 size_t numQubits = 2;
160 if (simulator) {
161 simType = simulator->GetType();
162 method = simulator->GetSimulationType();
163 numQubits = simulator->GetNumberOfQubits();
164 }
165
166 recreateIfNeeded = false;
167
168 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
169
170 recreateIfNeeded = recreate;
171
172 // put the results in the state
173 if (!res.empty()) {
174 const auto &first = *res.begin();
175 GetState().SetResultsInOrder(first.first);
176 }
177
178 if (recreate &&
179 (!simulator ||
180 (simulator && (simType != simulator->GetType() ||
181 method != simulator->GetSimulationType() ||
182 simulator->GetNumberOfQubits() != numQubits))))
183 CreateSimulator(simType, method);
184 }
185
201 std::vector<double> ExecuteExpectations(
202 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
203 const std::vector<std::string> &paulis) override {
204 const auto recreate = recreateIfNeeded;
205
208 size_t numQubits = 2;
209 if (simulator) {
210 simType = simulator->GetType();
211 method = simulator->GetSimulationType();
212 numQubits = simulator->GetNumberOfQubits();
213 }
214
215 recreateIfNeeded = false;
216
217 pauliStrings = &paulis;
218 const auto res = RepeatedExecute(circuit, 1);
219 pauliStrings = nullptr;
220
221 recreateIfNeeded = recreate;
222
223 // put the results in the state
224 if (!res.empty()) {
225 const auto &first = *res.begin();
226 GetState().SetResultsInOrder(first.first);
227 }
228
229 std::vector<double> expectations(paulis.size());
230 if (simulator) {
231 // translate the pauli strings to the mapped order of qubits
232 const size_t numOps = simulator->GetNumberOfQubits();
233
234 auto optimiser = controller->GetOptimiser();
235 if (optimiser) {
236 // convert the classical state results back to the expected order
237 const auto &qubitsMap = optimiser->GetQubitsMap();
238
239 for (size_t i = 0; i < paulis.size(); ++i) {
240 std::string translated(numOps, 'I');
241
242 for (size_t j = 0; j < paulis[i].size(); ++j) {
243 const auto pos = qubitsMap.find(j);
244 if (pos != qubitsMap.end())
245 translated[pos->second] = paulis[i][j];
246 else
247 translated[j] = paulis[i][j];
248 }
249
250 expectations[i] = simulator->ExpectationValue(translated);
251 }
252 } else {
253 for (size_t i = 0; i < paulis.size(); ++i)
254 expectations[i] = simulator->ExpectationValue(paulis[i]);
255 }
256 }
257
258 if (recreate && (!simulator || simType != simulator->GetType() ||
259 method != simulator->GetSimulationType() ||
260 simulator->GetNumberOfQubits() != numQubits))
261 CreateSimulator(simType, method);
262
263 return expectations;
264 }
265
281 std::vector<double> ExecuteOnHostExpectations(
282 const std::shared_ptr<Circuits::Circuit<Time>> &circuit, size_t hostId,
283 const std::vector<std::string> &paulis) override {
284 const auto recreate = recreateIfNeeded;
285
288 size_t numQubits = 2;
289 if (simulator) {
290 simType = simulator->GetType();
291 method = simulator->GetSimulationType();
292 numQubits = simulator->GetNumberOfQubits();
293 }
294
295 // RAII: restore recreateIfNeeded and clear pauliStrings on any exit path.
296 struct ScopedRestore {
297 bool &flag, saved;
298 const std::vector<std::string> **ps;
299 ScopedRestore(bool &f, const std::vector<std::string> **p)
300 : flag(f), saved(f), ps(p) {
301 flag = false;
302 }
303 ~ScopedRestore() {
304 flag = saved;
305 *ps = nullptr;
306 }
307 } restoreGuard(recreateIfNeeded, &pauliStrings);
308
309 pauliStrings = &paulis;
310 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
311
312 // put the results in the state
313 if (!res.empty()) {
314 const auto &first = *res.begin();
315 GetState().SetResultsInOrder(first.first);
316 }
317
318 // for (const auto& m : qubitsMapOnHost)
319 // std::cout << "Mapping qubit " << m.first << " to " << m.second <<
320 // std::endl;
321
322 const size_t offsetBase = qubitsMapOnHost.size();
323
324 std::vector<double> expectations(paulis.size(), 1.);
325 if (simulator) {
326 // translate the pauli strings to the mapped order of qubits
327 const size_t numOps = simulator->GetNumberOfQubits();
328
329 // convert the pauli strings to the actual qubits order
330 for (size_t i = 0; i < paulis.size(); ++i) {
331 std::string translated(std::max(numOps, paulis[i].size()), 'I');
332
333 size_t offset = offsetBase;
334
335 for (size_t j = 0; j < paulis[i].size(); ++j) {
336 auto pos = qubitsMapOnHost.find(j);
337 if (pos != qubitsMapOnHost.end())
338 translated[pos->second] = paulis[i][j];
339 else {
340 translated[offset] = paulis[i][j];
341 ++offset;
342 }
343 }
344
345 // std::cout << "Translated pauli string: " << translated << std::endl;
346
347 expectations[i] = simulator->ExpectationValue(translated);
348 }
349 } else {
350 throw std::runtime_error(
351 "ExecuteOnHostExpectations: no simulator available after execution.");
352 }
353
354 if (recreate && (!simulator || simType != simulator->GetType() ||
355 method != simulator->GetSimulationType() ||
356 simulator->GetNumberOfQubits() != numQubits))
357 CreateSimulator(simType, method);
358
359 return expectations;
360 }
361
373 std::vector<std::complex<double>> ExecuteOnHostAmplitudes(
374 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
375 size_t hostId) override {
376 const auto recreate = recreateIfNeeded;
377
380 size_t numQubits = 2;
381 if (simulator) {
382 simType = simulator->GetType();
383 method = simulator->GetSimulationType();
384 numQubits = simulator->GetNumberOfQubits();
385 }
386
387 // RAII: restore recreateIfNeeded on any exit path (including exceptions).
388 struct ScopedRestoreFlag {
389 bool &flag, saved;
390 ScopedRestoreFlag(bool &f) : flag(f), saved(f) { flag = false; }
391 ~ScopedRestoreFlag() { flag = saved; }
392 } restoreGuard(recreateIfNeeded);
393
394 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
395
396 if (!res.empty()) {
397 const auto &first = *res.begin();
398 GetState().SetResultsInOrder(first.first);
399 }
400
401 if (!simulator)
402 throw std::runtime_error(
403 "ExecuteOnHostAmplitudes: no simulator available after execution.");
404
405 std::vector<std::complex<double>> amplitudes;
406 const size_t n = simulator->GetNumberOfQubits();
407 const size_t dim = 1ULL << n;
408 amplitudes.resize(dim);
409 for (size_t state = 0; state < dim; ++state)
410 amplitudes[state] = simulator->Amplitude(state);
411
412 // Remap amplitudes back to the original qubit ordering if qubits were
413 // remapped during execution on the host.
414 if (!qubitsMapOnHost.empty()) {
415 const size_t offsetBase = qubitsMapOnHost.size();
416
417 // Build reverse mapping: simulator qubit position -> original qubit
418 // position.
419 std::vector<size_t> simToOrig(n);
420 size_t offset = offsetBase;
421
422 for (size_t qbit = 0; qbit < n; ++qbit) {
423 auto pos = qubitsMapOnHost.find(qbit);
424 if (pos != qubitsMapOnHost.end())
425 simToOrig[pos->second] = pos->first;
426 else
427 simToOrig[qbit] = offset++;
428 }
429
430 std::vector<std::complex<double>> remapped(dim);
431
432 for (size_t sim_state = 0; sim_state < dim; ++sim_state) {
433 size_t orig_state = 0;
434 for (size_t qbit = 0; qbit < n; ++qbit) {
435 if (sim_state & (1ULL << qbit))
436 orig_state |= (1ULL << simToOrig[qbit]);
437 }
438 if (orig_state < dim) remapped[orig_state] = amplitudes[sim_state];
439 }
440 amplitudes.swap(remapped);
441 }
442
443 if (recreate && (!simulator || simType != simulator->GetType() ||
444 method != simulator->GetSimulationType() ||
445 simulator->GetNumberOfQubits() != numQubits))
446 CreateSimulator(simType, method);
447
448 return amplitudes;
449 }
450
464 std::complex<double> ExecuteOnHostProjectOnZero(
465 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
466 size_t hostId) override {
467 const auto recreate = recreateIfNeeded;
468
471 size_t numQubits = 2;
472 if (simulator) {
473 simType = simulator->GetType();
474 method = simulator->GetSimulationType();
475 numQubits = simulator->GetNumberOfQubits();
476 }
477
478 // RAII: restore recreateIfNeeded on any exit path (including exceptions).
479 struct ScopedRestoreFlag {
480 bool &flag, saved;
481 ScopedRestoreFlag(bool &f) : flag(f), saved(f) { flag = false; }
482 ~ScopedRestoreFlag() { flag = saved; }
483 } restoreGuard(recreateIfNeeded);
484
485 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
486
487 if (!res.empty()) {
488 const auto &first = *res.begin();
489 GetState().SetResultsInOrder(first.first);
490 }
491
492 if (!simulator)
493 throw std::runtime_error(
494 "ExecuteOnHostProjectOnZero: no simulator available after "
495 "execution.");
496
497 const std::complex<double> result = simulator->ProjectOnZero();
498
499 if (recreate && (!simulator || simType != simulator->GetType() ||
500 method != simulator->GetSimulationType() ||
501 simulator->GetNumberOfQubits() != numQubits))
502 CreateSimulator(simType, method);
503
504 return result;
505 }
506
521 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
522 size_t shots = 1000) override {
523 if (!controller || !circuit) return {};
524
525 distCirc = controller->DistributeCircuit(BaseClass::getptr(), circuit);
526 if (!distCirc) return {};
527
528#ifdef _DEBUG
529 for (auto q : distCirc->AffectedQubits()) {
530 if (q >= GetNumQubits()) {
531 std::cout
532 << "This is a distributed circuit, using entanglement or cutting"
533 << std::endl;
534 break;
535 }
536 }
537#endif
538
539 if (!simulator) return {};
540
541 auto simType = simulator->GetType();
542 if (distCirc->HasOpsAfterMeasurements() &&
543 (
544#ifndef NO_QISKIT_AER
546#endif
548 distCirc->MoveMeasurementsAndResets();
549
550 auto method = simulator->GetSimulationType();
551
552 const auto saveSimType = simType;
553 const auto saveMethod = method;
554
555 if (GetOptimizeSimulator() && distCirc->IsClifford() &&
557 // this is for the gpu simulator, as it doesn't support stabilizer
558#ifdef __linux__
560#endif
561 ) {
563
566#ifndef NO_QISKIT_AER
569#endif
570 }
571
572 ExecuteResults res;
573 const size_t nrQubits = GetNumQubits() + GetNumNetworkEntangledQubits();
574 const size_t nrCbitsResults = GetNumClassicalBits();
575
576 maxBondDim =
577 simulator->GetConfiguration("matrix_product_state_max_bond_dimension");
578 singularValueThreshold = simulator->GetConfiguration(
579 "matrix_product_state_truncation_threshold");
580 mpsSample = simulator->GetConfiguration("mps_sample_measure_algorithm");
581
582 // do that only if the optimization for simulator is on and the estimator is
583 // available, ortherwise an 'optimal' simulator won't be created
585 simulatorsEstimator->IsInitialized()) {
586 simulator->Clear();
587 GetState().Clear();
588 }
589
590 std::vector<bool> executed;
591 auto optSim =
592 ChooseBestSimulator(distCirc, shots, nrQubits, nrQubits, nrCbitsResults,
593 simType, method, executed);
594
595 lastSimulatorType = simType;
596 lastMethod = method;
597
598 size_t nrThreads = GetMaxSimulators();
599
600#ifdef __linux__
602 nrThreads = 1;
603 else
604#endif
606 !distCirc->HasOpsAfterMeasurements()) ||
608 nrThreads = 1;
609
610 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
611
612 std::mutex resultsMutex;
613
614 auto dcirc = distCirc;
615
616 if (nrThreads > 1) {
617 // since it's going to execute on multiple threads, free the memory from
618 // the network's simulator and state, it's going to use other ones,
619 // created in the threads if optimization already exists, it will be
620 // cloned in the threads, otherwise a new one will be created in the
621 // threads
624 ->IsInitialized()) // otherwise it was already cleared
625 {
626 simulator->Clear();
627 GetState().Clear();
628 }
629
630 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
631
632 threadsPool.Resize(nrThreads);
633 threadsPool.SetFinishLimit(shots);
634
635 while (shots > 0) {
636 const size_t curCnt = std::min(cntPerThread, shots);
637
638 shots -= curCnt;
639
640 auto job = std::make_shared<ExecuteJob<Time>>(
641 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
642 method, resultsMutex);
643 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
644
645 job->maxBondDim = maxBondDim;
646 job->mpsSample = mpsSample;
647 job->singularValueThreshold = singularValueThreshold;
648
649 job->network = BaseClass::getptr();
650
651 if (optSim) {
652 job->optSim = optSim->Clone();
653 job->executedGates = executed;
654 }
655
656 threadsPool.AddRunJob(std::move(job));
657 }
658
659 threadsPool.WaitForFinish();
660 threadsPool.Stop();
661 } else {
662 const size_t curCnt = shots;
663
664 auto job = std::make_shared<ExecuteJob<Time>>(
665 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
666 method, resultsMutex);
667 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
668
669 job->maxBondDim = maxBondDim;
670 job->mpsSample = mpsSample;
671 job->singularValueThreshold = singularValueThreshold;
672
673 job->network = BaseClass::getptr();
674
675 if (optSim) {
676 optSim->SetMultithreading(true);
677 job->optSim = optSim;
678 job->executedGates = executed;
679 } else {
680 if (simulator && method == saveMethod && simType == saveSimType) {
681 // use the already created simulator
682 optSim = simulator;
683 job->optSim = optSim;
684 OptimizeMPSInitialQubitsMap(optSim, dcirc,
685 optSim->GetNumberOfQubits());
686 job->executedGates.resize(dcirc->size(),
687 false); // no gates executed yet
688 simulator = nullptr;
689 }
690 }
691
692 job->DoWorkNoLock();
693 if (!recreateIfNeeded) simulator = job->optSim;
694 }
695
696 if (recreateIfNeeded) CreateSimulator(saveSimType, saveMethod);
697
699
700 return res;
701 }
702
719 const std::shared_ptr<Circuits::Circuit<Time>> &circuit, size_t hostId,
720 size_t shots = 1000) override {
721 if (!circuit || hostId >= GetNumHosts()) return {};
722
723 size_t nrQubits = 0;
724 size_t nrCbits = 0;
725
726 std::shared_ptr<Circuits::Circuit<Time>> optCircuit;
727 if (GetController()->GetOptimizeCircuit()) {
728 optCircuit =
729 std::static_pointer_cast<Circuits::Circuit<Time>>(circuit->Clone());
730 optCircuit->Optimize();
731 }
732 const auto reverseQubitsMap = MapCircuitOnHost(
733 GetController()->GetOptimizeCircuit() ? optCircuit : circuit, hostId,
734 nrQubits, nrCbits, true);
735 if (nrCbits == 0) nrCbits = nrQubits;
736
737 if (!simulator || !distCirc) return {};
738
739 auto simType = simulator->GetType();
740
741 maxBondDim =
742 simulator->GetConfiguration("matrix_product_state_max_bond_dimension");
743 singularValueThreshold = simulator->GetConfiguration(
744 "matrix_product_state_truncation_threshold");
745 mpsSample = simulator->GetConfiguration("mps_sample_measure_algorithm");
746
747 if (distCirc->HasOpsAfterMeasurements() &&
748 (
749#ifndef NO_QISKIT_AER
751#endif
753 distCirc->MoveMeasurementsAndResets();
754
755 auto method = simulator->GetSimulationType();
756 const auto saveSimType = simType;
757 const auto saveMethod = method;
758
759 if (GetOptimizeSimulator() && distCirc->IsClifford() &&
761 // this is for the gpu simulator, as it doesn't support stabilizer
762#ifdef __linux__
764#endif
765 ) {
767
770#ifndef NO_QISKIT_AER
773#endif
774 }
775
776 ExecuteResults res;
777
778 // since it's going to execute on multiple threads, free the memory from the
779 // network's simulator and state, it's going to use other ones, created in
780 // the threads
781 simulator->Clear();
782 GetState().Clear();
783
784 std::vector<bool> executed;
785 auto optSim = ChooseBestSimulator(distCirc, shots, nrQubits, nrCbits,
786 nrCbits, simType, method, executed);
787
788 lastSimulatorType = simType;
789 lastMethod = method;
790
791 size_t nrThreads = GetMaxSimulators();
792
793#ifdef __linux__
795 nrThreads = 1;
796 else
797#endif
800 !distCirc->HasOpsAfterMeasurements()) ||
802 nrThreads = 1;
803
804 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
805
806 // WARNING: be sure to not put this above ChooseBestSimulator, as that one
807 // can change the shots value!
808
809 std::mutex resultsMutex;
810
811 const auto dcirc = distCirc;
812
813 if (nrThreads > 1) {
814 // this rounds up, rounding down is better
815 // const size_t cntPerThread = static_cast<size_t>((shots - 1) / nrThreads
816 // + 1);
817 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
818
819 threadsPool.Resize(nrThreads);
820 threadsPool.SetFinishLimit(shots);
821
822 while (shots > 0) {
823 const size_t curCnt = std::min(cntPerThread, shots);
824 shots -= curCnt;
825
826 auto job = std::make_shared<ExecuteJob<Time>>(
827 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
828 resultsMutex);
829 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
830
831 job->maxBondDim = maxBondDim;
832 job->mpsSample = mpsSample;
833 job->singularValueThreshold = singularValueThreshold;
834
835 job->network = BaseClass::getptr();
836
837 if (optSim) {
838 job->optSim = optSim->Clone();
839 job->executedGates = executed;
840 }
841
842 threadsPool.AddRunJob(std::move(job));
843 }
844
845 threadsPool.WaitForFinish();
846 threadsPool.Stop();
847 } else {
848 const size_t curCnt = shots;
849
850 auto job = std::make_shared<ExecuteJob<Time>>(
851 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
852 resultsMutex);
853 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
854
855 job->maxBondDim = maxBondDim;
856 job->mpsSample = mpsSample;
857 job->singularValueThreshold = singularValueThreshold;
858
859 job->network = BaseClass::getptr();
860
861 if (optSim) {
862 optSim->SetMultithreading(true);
863 job->optSim = optSim;
864 job->executedGates = executed;
865 }
866
867 job->DoWorkNoLock();
868 if (!recreateIfNeeded) simulator = job->optSim;
869 }
870
871 if (recreateIfNeeded) CreateSimulator(saveSimType, saveMethod);
872
873 if (!reverseQubitsMap.empty()) ConvertBackResults(res, reverseQubitsMap);
874
875 return res;
876 }
877
887 const std::shared_ptr<Circuits::Circuit<Time>> &circuit) const override {
888 if (!circuit) return 0;
889
890 size_t distgates = 0;
891
892 for (const auto &op : circuit->GetOperations())
893 if (!IsLocalOperation(op)) ++distgates;
894
895 return distgates;
896 }
897
914 std::vector<ExecuteResults> ExecuteScheduled(
915 const std::vector<Schedulers::ExecuteCircuit<Time>> &circuits) override {
916 // create a default one if not set
917 if (!GetScheduler()) {
919
920 if (!GetScheduler()) return {};
921 }
922
923 return GetScheduler()->ExecuteScheduled(circuits);
924 }
925
947 Simulators::SimulationType simExecType =
949 size_t nrQubits = 0) override {
950 classicalState.Clear();
951 classicalState.AllocateBits(GetNumClassicalBits() +
953
954 simulator =
956
957 if (simulator) {
958 if (!maxBondDim.empty())
959 simulator->Configure("matrix_product_state_max_bond_dimension",
960 maxBondDim.c_str());
961 if (!singularValueThreshold.empty())
962 simulator->Configure("matrix_product_state_truncation_threshold",
963 singularValueThreshold.c_str());
964 if (!mpsSample.empty())
965 simulator->Configure("mps_sample_measure_algorithm", mpsSample.c_str());
966 if (useDoublePrecision) simulator->Configure("use_double_precision", "1");
967
968 simulator->AllocateQubits(
969 nrQubits == 0 ? GetNumQubits() + GetNumNetworkEntangledQubits()
970 : nrQubits);
971 simulator->Initialize();
972
973 simulator->setGrowthFactorGate(growthFactorGate);
974 simulator->setGrowthFactorSwap(growthFactorSwap);
975 simulator->SetLookaheadDepth(lookaheadDepth);
976 simulator->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
977 }
978 }
979
989 void Configure(const char *key, const char *value) override {
990 if (!key || !value) return;
991
992 if (std::string("matrix_product_state_max_bond_dimension") == key)
993 maxBondDim = value;
994 else if (std::string("matrix_product_state_truncation_threshold") == key)
996 else if (std::string("mps_sample_measure_algorithm") == key)
997 mpsSample = value;
998 else if (std::string("use_double_precision") == key)
1000 (std::string("1") == value || std::string("true") == value);
1001 else if (std::string("max_simulators") == key)
1002 maxSimulators = std::stoull(value);
1003
1004 if (simulator) simulator->Configure(key, value);
1005 }
1006
1015 std::shared_ptr<Simulators::ISimulator> GetSimulator() const override {
1016 return simulator;
1017 }
1018
1028
1041 SchedulerType schType =
1043 if (!controller) return;
1044
1045 controller->CreateScheduler(BaseClass::getptr(), schType);
1046 }
1047
1056 std::shared_ptr<Schedulers::IScheduler<Time>> GetScheduler() const override {
1057 if (!controller) return nullptr;
1058
1059 return controller->GetScheduler();
1060 }
1061
1071 const std::shared_ptr<IHost<Time>> GetHost(size_t hostId) const override {
1072 if (hostId >= hosts.size()) return nullptr;
1073
1074 return hosts[hostId];
1075 }
1076
1085 const std::shared_ptr<IController<Time>> GetController() const override {
1086 return controller;
1087 }
1088
1096 size_t GetNumHosts() const override { return hosts.size(); }
1097
1106 size_t GetNumQubits() const override {
1107 size_t res = 0;
1108
1109 for (const auto &host : hosts) res += host->GetNumQubits();
1110
1111 return res;
1112 }
1113
1123 size_t GetNumQubitsForHost(size_t hostId) const override {
1124 if (hostId >= hosts.size()) return 0;
1125
1126 return hosts[hostId]->GetNumQubits();
1127 }
1128
1137 size_t GetNumNetworkEntangledQubits() const override {
1138 size_t res = 0;
1139
1140 for (const auto &host : hosts) res += host->GetNumNetworkEntangledQubits();
1141
1142 return res;
1143 }
1144
1157 size_t GetNumNetworkEntangledQubitsForHost(size_t hostId) const override {
1158 if (hostId >= hosts.size()) return 0;
1159
1160 return hosts[hostId]->GetNumNetworkEntangledQubits();
1161 }
1162
1171 size_t GetNumClassicalBits() const override {
1172 size_t res = 0;
1173
1174 for (const auto &host : hosts) res += host->GetNumClassicalBits();
1175
1176 return res;
1177 }
1178
1190 size_t GetNumClassicalBitsForHost(size_t hostId) const override {
1191 if (hostId >= hosts.size()) return 0;
1192
1193 return hosts[hostId]->GetNumClassicalBits();
1194 }
1195
1205 std::vector<std::shared_ptr<IHost<Time>>> &GetHosts() { return hosts; }
1206
1214 void SetController(const std::shared_ptr<IController<Time>> &cntrl) {
1215 controller = cntrl;
1216 }
1217
1231 bool SendPacket(size_t fromHostId, size_t toHostId,
1232 const std::vector<uint8_t> &packet) override {
1233 return false;
1234 }
1235
1243 NetworkType GetType() const override {
1245 }
1246
1258 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1259 const auto qubits = op->AffectedQubits();
1260
1261 if (qubits.empty()) return true;
1262
1263 size_t firstQubit = qubits[0];
1264
1265 for (size_t q = 1; q < qubits.size(); ++q)
1266 if (!AreQubitsOnSameHost(firstQubit, qubits[q])) return false;
1267
1268 return true;
1269 }
1270
1284 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1285 const auto qubits = op->AffectedQubits();
1286
1287 if (qubits.empty()) return false;
1288
1289 // grab the first qubit that is on a host (skip over network entangled
1290 // qubits)
1291 size_t firstQubit = qubits[0];
1292 size_t q = 1;
1293 for (; IsNetworkEntangledQubit(firstQubit) && q < qubits.size(); ++q)
1294 firstQubit = qubits[q];
1295
1296 // check to see one of the other qubits is on a different host, but ignore
1297 // the network entangled qubits
1298 for (; q < qubits.size(); ++q)
1299 if (!IsNetworkEntangledQubit(qubits[q]) &&
1300 !AreQubitsOnSameHost(firstQubit, qubits[q]))
1301 return true;
1302
1303 return false;
1304 }
1305
1318 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1319 const auto qubits = op->AffectedQubits();
1320
1321 if (qubits.empty()) return false;
1322
1323 for (size_t q = 0; q < qubits.size(); ++q)
1324 if (IsNetworkEntangledQubit(qubits[q])) return true;
1325
1326 return false;
1327 }
1328
1339 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1340 if (op->GetType() != Circuits::OperationType::kGate) return false;
1341 const auto qubits = op->AffectedQubits();
1342 if (qubits.size() != 2) return false;
1343
1344 return IsNetworkEntangledQubit(qubits[0]) &&
1345 IsNetworkEntangledQubit(qubits[1]);
1346 }
1347
1359 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1360 if (!op->IsConditional()) return false;
1361
1362 const auto qubits = op->AffectedQubits();
1363
1364 const std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1365 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1366 const auto &classicalBits = condOp->GetCondition()->GetBitsIndices();
1367
1368 if (qubits.empty() && classicalBits.empty())
1369 throw std::runtime_error(
1370 "No classical bits specified!"); // this would be odd!
1371
1372 // consider it on the host where it has the first qubit (or bit, if there
1373 // are no qubits)
1374
1375 const size_t hostId = GetHostIdForAnyQubit(qubits[0]);
1376
1377 // now check the classical bits
1378 for (const auto bit : classicalBits)
1379 if (hostId != GetHostIdForClassicalBit(bit)) return true;
1380
1381 return false;
1382 }
1383
1395 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1396 if (!op->IsConditional())
1397 throw std::runtime_error("Operation is not conditional!");
1398
1399 std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1400 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1401 const auto classicalBits = condOp->AffectedBits();
1402
1403 if (classicalBits.empty())
1404 throw std::runtime_error("No classical bits specified!");
1405
1406 return GetHostIdForClassicalBit(classicalBits[0]);
1407 }
1408
1417 bool AreQubitsOnSameHost(size_t qubitId1, size_t qubitId2) const override {
1418 for (const auto &host : hosts) {
1419 const bool present1 = host->IsQubitOnHost(qubitId1);
1420 const bool present2 = host->IsQubitOnHost(qubitId2);
1421
1422 if (present1 && present2)
1423 return true;
1424 else if (present1 || present2)
1425 return false;
1426 }
1427
1428 return false;
1429 }
1430
1440 bool AreClassicalBitsOnSameHost(size_t bitId1, size_t bitId2) const override {
1441 for (const auto &host : hosts) {
1442 const bool present1 = host->IsClassicalBitOnHost(bitId1);
1443 const bool present2 = host->IsClassicalBitOnHost(bitId2);
1444
1445 if (present1 && present2)
1446 return true;
1447 else if (present1 || present2)
1448 return false;
1449 }
1450
1451 return false;
1452 }
1453
1465 size_t bitId) const override {
1466 for (const auto &host : hosts) {
1467 const bool present1 = host->IsQubitOnHost(qubitId);
1468 const bool present2 = host->IsClassicalBitOnHost(bitId);
1469
1470 if (present1 && present2)
1471 return true;
1472 else if (present1 || present2)
1473 return false;
1474 }
1475
1476 return false;
1477 }
1478
1488 size_t GetHostIdForQubit(size_t qubitId) const override {
1489 for (const auto &host : hosts)
1490 if (host->IsQubitOnHost(qubitId)) return host->GetId();
1491
1492 return std::numeric_limits<size_t>::max();
1493 }
1494
1507 size_t GetHostIdForEntangledQubit(size_t qubitId) const override {
1508 for (const auto &host : hosts)
1509 if (host->IsEntangledQubitOnHost(qubitId)) return host->GetId();
1510
1511 return std::numeric_limits<size_t>::max();
1512 }
1513
1523 size_t GetHostIdForAnyQubit(size_t qubitId) const override {
1524 if (IsNetworkEntangledQubit(qubitId))
1525 return GetHostIdForEntangledQubit(qubitId);
1526
1527 return GetHostIdForQubit(qubitId);
1528 }
1529
1539 size_t GetHostIdForClassicalBit(size_t classicalBitId) const override {
1540 for (const auto &host : hosts)
1541 if (host->IsClassicalBitOnHost(classicalBitId)) return host->GetId();
1542
1543 return std::numeric_limits<size_t>::max();
1544 }
1545
1554 std::vector<size_t> GetQubitsIds(size_t hostId) const override {
1555 if (hostId >= hosts.size()) return std::vector<size_t>();
1556
1557 return hosts[hostId]->GetQubitsIds();
1558 }
1559
1570 std::vector<size_t> GetNetworkEntangledQubitsIds(
1571 size_t hostId) const override {
1572 if (hostId >= hosts.size()) return std::vector<size_t>();
1573
1574 return hosts[hostId]->GetNetworkEntangledQubitsIds();
1575 }
1576
1586 std::vector<size_t> GetClassicalBitsIds(size_t hostId) const override {
1587 if (hostId >= hosts.size()) return std::vector<size_t>();
1588
1589 return hosts[hostId]->GetClassicalBitsIds();
1590 }
1591
1603 size_t hostId) const override {
1604 if (hostId >= hosts.size()) return std::vector<size_t>();
1605
1606 return hosts[hostId]->GetEntangledQubitMeasurementBitIds();
1607 }
1608
1620 bool IsNetworkEntangledQubit(size_t qubitId) const override {
1621 return qubitId >= GetNumQubits();
1622 }
1623
1636 bool IsEntanglementQubitBusy(size_t qubitId) const override { return false; }
1637
1653 bool AreEntanglementQubitsBusy(size_t qubitId1,
1654 size_t qubitId2) const override {
1655 return false;
1656 }
1657
1670 void MarkEntangledQubitsBusy(size_t qubitId1, size_t qubitId2) override {
1671 throw std::runtime_error(
1672 "Entanglement between hosts is not supported in the simple network");
1673 }
1674
1686 void MarkEntangledQubitFree(size_t qubitId) override {
1687 throw std::runtime_error(
1688 "Entanglement between hosts is not supported in the simple network");
1689 }
1690
1700 void ClearEntanglements() override {
1701 throw std::runtime_error(
1702 "Entanglement between hosts is not supported in the simple network");
1703 }
1704
1714 std::shared_ptr<Circuits::Circuit<Time>> GetDistributedCircuit()
1715 const override {
1716 return distCirc;
1717 }
1718
1729
1738 return lastMethod;
1739 }
1740
1751 size_t GetMaxSimulators() const override { return maxSimulators; }
1752
1764 void SetMaxSimulators(size_t val) override {
1765 if (val < 1)
1766 val = 1;
1767 else if (val > (size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads())
1768 val = (size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads();
1769
1770 maxSimulators = val;
1771 }
1772
1782 void SetOptimizeSimulator(bool optimize = true) override {
1783 optimizeSimulator = optimize;
1784 }
1785
1793 bool GetOptimizeSimulator() const override { return optimizeSimulator; }
1794
1803 const typename BaseClass::SimulatorsSet &GetSimulatorsSet() const override {
1805 }
1806
1817 Simulators::SimulationType kind) override {
1818 simulatorsForOptimizations.insert({type, kind});
1819 }
1820
1833
1850
1863 Simulators::SimulationType kind) const override {
1864 if (simulatorsForOptimizations.empty()) return true;
1865
1866 return simulatorsForOptimizations.find({type, kind}) !=
1868 }
1869
1876 std::shared_ptr<INetwork<Time>> Clone() const override {
1877 const size_t numHosts = GetNumHosts();
1878
1879 std::vector<Types::qubit_t> qubits(numHosts);
1880 std::vector<size_t> cbits(numHosts);
1881
1882 for (size_t h = 0; h < numHosts; ++h) {
1883 qubits[h] = GetNumQubitsForHost(h);
1884 cbits[h] = GetNumClassicalBitsForHost(h);
1885 }
1886
1887 const auto cloned =
1888 std::make_shared<SimpleDisconnectedNetwork<Time, Controller>>(qubits,
1889 cbits);
1890
1891 cloned->maxBondDim = maxBondDim;
1892 cloned->singularValueThreshold = singularValueThreshold;
1893 cloned->mpsSample = mpsSample;
1894
1895 cloned->optimizeSimulator = optimizeSimulator;
1896 cloned->simulatorsForOptimizations = simulatorsForOptimizations;
1897
1898 cloned->SetMPSOptimizeSwaps(GetMPSOptimizeSwaps());
1899
1900 cloned->SetMPSOptimizationBondDimensionThreshold(GetMPSOptimizationBondDimensionThreshold());
1901 cloned->SetMPSOptimizationQubitsNumberThreshold(GetMPSOptimizationQubitsNumberThreshold());
1902
1903 cloned->SetLookaheadDepth(GetLookaheadDepth());
1904 cloned->SetLookaheadDepthWithHeuristic(GetLookaheadDepthWithHeuristic());
1905
1906 cloned->setGrowthFactorGate(getGrowthFactorGate());
1907 cloned->setGrowthFactorSwap(getGrowthFactorSwap());
1908
1909 if (GetSimulator())
1910 cloned->CreateSimulator(GetSimulator()->GetType(),
1912
1913 return cloned;
1914 }
1915
1916 std::shared_ptr<Simulators::ISimulator> ChooseBestSimulator(
1917 std::shared_ptr<Circuits::Circuit<Time>> &dcirc, size_t &counts,
1918 size_t nrQubits, size_t nrCbits, size_t nrResultCbits,
1920 std::vector<bool> &executed, bool multithreading = false,
1921 bool dontRunCircuitStart = false) const override {
1922 if (!optimizeSimulator) return nullptr;
1923
1924 if ((!simulatorsEstimator || !simulatorsEstimator->IsInitialized()) &&
1925 simulatorsForOptimizations.size() != 1)
1926 return nullptr;
1927
1928 // when multithreading is set to true it means it needs a multithreaded
1929 // simulator
1930
1931 std::vector<
1932 std::pair<Simulators::SimulatorType, Simulators::SimulationType>>
1933 simulatorTypes;
1934
1935 const bool checkTensorNetwork =
1937
1938 // the others are to be picked between statevector, composite, tensor
1939 // networks and mps, for now at least for tensor networks in the future it's
1940 // worth checking different contractors!!!!
1941 //
1942 // clifford was decided at higher level
1944 // compare qcsim with qiskit aer if qiskit aer is available, let the best
1945 // one win
1948 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1950
1951#ifndef NO_QISKIT_AER
1952 // if the number of shots is too small, probably it's not worth it, it's
1953 // going to be better to just execute them multithreading
1956 simulatorTypes.emplace_back(Simulators::SimulatorType::kQiskitAer,
1958#endif
1959 }
1960
1963 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1965
1968 simulatorTypes.emplace_back(Simulators::SimulatorType::kCompositeQCSim,
1970
1971 if (checkTensorNetwork &&
1974 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1976
1980 (nrQubits <= 4 || !maxBondDim.empty()))
1981 simulatorTypes.emplace_back(
1984
1988 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1990
1994 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1996
1997#ifndef NO_QISKIT_AER
1998 // tensor networks are out of the picture for now for qiskit aer, since they
1999 // are available with cuda library, and work only on linux (obviously when
2000 // compiled properly and if there is the right hw an driver installed)
2001
2004 simulatorTypes.emplace_back(Simulators::SimulatorType::kQiskitAer,
2006
2010 simulatorTypes.emplace_back(
2013
2017 (nrQubits <= 4 || !maxBondDim.empty()))
2018 simulatorTypes.emplace_back(
2021#endif
2022
2023#ifdef __linux__
2027 simulatorTypes.emplace_back(Simulators::SimulatorType::kGpuSim,
2032 simulatorTypes.emplace_back(
2038 simulatorTypes.emplace_back(Simulators::SimulatorType::kGpuSim,
2043 simulatorTypes.emplace_back(
2046 }
2047#endif
2048
2051 simulatorTypes.emplace_back(Simulators::SimulatorType::kQuestSim,
2053
2054 if (simulatorTypes.empty())
2055 return nullptr;
2056 else if (simulatorTypes.size() == 1) {
2057 simType = simulatorTypes[0].first;
2058 method = simulatorTypes[0].second;
2059
2060 std::shared_ptr<Simulators::ISimulator> sim =
2062 if (sim) {
2064 if (!maxBondDim.empty())
2065 sim->Configure("matrix_product_state_max_bond_dimension",
2066 maxBondDim.c_str());
2067 if (!singularValueThreshold.empty())
2068 sim->Configure("matrix_product_state_truncation_threshold",
2069 singularValueThreshold.c_str());
2070 if (!mpsSample.empty())
2071 sim->Configure("mps_sample_measure_algorithm", mpsSample.c_str());
2072
2073 sim->AllocateQubits(nrQubits);
2074 sim->Initialize();
2075
2076 sim->setGrowthFactorGate(growthFactorGate);
2077 sim->setGrowthFactorSwap(growthFactorSwap);
2078 sim->SetLookaheadDepth(lookaheadDepth);
2079 sim->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
2080
2081 OptimizeMPSInitialQubitsMap(sim, dcirc, nrQubits);
2082 } else {
2083 sim->AllocateQubits(nrQubits);
2084 sim->Initialize();
2085 }
2086
2087 if (!dontRunCircuitStart) {
2088 sim->SetMultithreading(true);
2090 Time>::ExecuteUpToMeasurements(dcirc, nrQubits, nrCbits,
2091 nrResultCbits, sim, executed);
2092 }
2093 sim->SetMultithreading(multithreading || GetMaxSimulators() == 1);
2094
2095 return sim;
2096 }
2097 }
2098
2099 std::shared_ptr<Simulators::ISimulator> sim =
2100 simulatorsEstimator->ChooseBestSimulator(
2101 simulatorTypes, dcirc, counts, nrQubits, nrCbits, nrResultCbits,
2102 simType, method, executed, maxBondDim, singularValueThreshold,
2103 mpsSample, GetMaxSimulators(), pauliStrings, multithreading);
2104
2105 if (sim) {
2106 sim->AllocateQubits(nrQubits);
2107 sim->Initialize();
2108
2109 sim->setGrowthFactorGate(growthFactorGate);
2110 sim->setGrowthFactorSwap(growthFactorSwap);
2111 sim->SetLookaheadDepth(lookaheadDepth);
2112 sim->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
2113
2114 OptimizeMPSInitialQubitsMap(sim, dcirc, nrQubits);
2115
2116 if (!dontRunCircuitStart) {
2117 sim->SetMultithreading(true);
2119 dcirc, nrQubits, nrCbits, nrResultCbits, sim, executed);
2120 }
2121 sim->SetMultithreading(multithreading || GetMaxSimulators() == 1);
2122 }
2123
2124 return sim;
2125 }
2126
2127 void SetInitialQubitsMapOptimization(bool optimize = true) override {
2128 optimizeInitialQubitsMap = optimize;
2129 }
2130
2131 bool GetInitialQubitsMapOptimization() const override {
2132 return optimizeInitialQubitsMap;
2133 }
2134
2135 void SetMPSOptimizeSwaps(bool optimize = true) override {
2136 mpsOptimizeSwaps = optimize;
2137
2138 if (simulator) {
2139 simulator->SetLookaheadDepth(0);
2140 simulator->SetLookaheadDepthWithHeuristic(0);
2141 }
2142 }
2143
2144 bool GetMPSOptimizeSwaps() const override { return mpsOptimizeSwaps; }
2145
2146 void SetMPSOptimizationBondDimensionThreshold(size_t threshold) override {
2147 mpsOptimizationBondDimensionThreshold = threshold;
2148
2149 if (simulator &&
2150 std::stoull(simulator->GetConfiguration(
2151 "matrix_product_state_max_bond_dimension")) < threshold) {
2152 simulator->SetLookaheadDepth(0);
2153 simulator->SetLookaheadDepthWithHeuristic(0);
2154 }
2155 }
2156
2158 return mpsOptimizationBondDimensionThreshold;
2159 }
2160
2161 void SetMPSOptimizationQubitsNumberThreshold(size_t threshold) override {
2162 mpsOptimizationQubitsNumberThreshold = threshold;
2163
2164 if (GetNumQubits() < threshold && simulator) {
2165 simulator->SetLookaheadDepth(0);
2166 simulator->SetLookaheadDepthWithHeuristic(0);
2167 }
2168 }
2169
2171 return mpsOptimizationQubitsNumberThreshold;
2172 }
2173
2174 void SetLookaheadDepth(int depth) override {
2175 if (depth < 0) depth = std::numeric_limits<int>::max();
2176
2177 lookaheadDepth = depth;
2178
2179 if (simulator && lookaheadDepth != std::numeric_limits<int>::max()) {
2180 simulator->SetLookaheadDepth(0);
2181 simulator->SetLookaheadDepthWithHeuristic(0);
2182 }
2183 }
2184
2185 int GetLookaheadDepth() const override { return lookaheadDepth; }
2186
2187 void SetLookaheadDepthWithHeuristic(int depth) override {
2188 if (depth < 0) depth = std::numeric_limits<int>::max();
2189
2190 if (depth > lookaheadDepth) depth = lookaheadDepth;
2191
2192 lookaheadDepthWithHeuristic = depth;
2193
2194 if (simulator && lookaheadDepthWithHeuristic != std::numeric_limits<int>::max())
2195 simulator->SetLookaheadDepthWithHeuristic(depth);
2196 }
2197
2198 int GetLookaheadDepthWithHeuristic() const override {
2199 return lookaheadDepthWithHeuristic;
2200 }
2201
2202 double getGrowthFactorSwap() const override { return growthFactorSwap; }
2203 double getGrowthFactorGate() const override { return growthFactorGate; }
2204
2205 void setGrowthFactorSwap(double factor) override {
2206 growthFactorSwap = factor;
2207
2208 if (simulator) simulator->setGrowthFactorSwap(factor);
2209 }
2210
2211 void setGrowthFactorGate(double factor) override {
2212 growthFactorGate = factor;
2213
2214 if (simulator) simulator->setGrowthFactorGate(factor);
2215 }
2216
2217 protected:
2219 std::shared_ptr<Simulators::ISimulator> &sim,
2220 std::shared_ptr<Circuits::Circuit<Time>> &dcirc, size_t nrQubits) const {
2221 if (sim->GetSimulationType() ==
2223 (optimizeInitialQubitsMap || mpsOptimizeSwaps) &&
2224 sim->SupportsMPSSwapOptimization()) {
2225 if (mpsOptimizationQubitsNumberThreshold <= nrQubits) {
2226 const auto maxBondDimValue =
2227 maxBondDim.empty() ? 0 : std::stoi(maxBondDim);
2228
2229 if (maxBondDim.empty() ||
2230 static_cast<int>(mpsOptimizationBondDimensionThreshold) <= maxBondDimValue) {
2231 // need to be sure the circuit is correctly converted
2232 dcirc->ConvertForCutting(); // convert the three qubit gates
2233 auto layers = dcirc->ToMultipleQubitsLayersNoClone();
2234
2235 Simulators::MPSDummySimulator dummySim(nrQubits);
2236 dummySim.setGrowthFactorGate(growthFactorGate);
2237 dummySim.setGrowthFactorSwap(growthFactorSwap);
2238
2239 if (!maxBondDim.empty())
2240 dummySim.SetMaxBondDimension(maxBondDimValue);
2241
2242 if (optimizeInitialQubitsMap) {
2243 const auto optimalMap = dummySim.ComputeOptimalQubitsMap(layers);
2244 sim->SetInitialQubitsMap(optimalMap);
2245 }
2246
2247 auto optCirc = Circuits::Circuit<Time>::LayersToCircuit(layers);
2248 dcirc->SetOperations(optCirc->GetOperations());
2249
2250 if (mpsOptimizeSwaps) {
2251 // TODO: come up with something better!
2252 int lookaheadDepthLocal = lookaheadDepth;
2253
2254 if (lookaheadDepthLocal == std::numeric_limits<int>::max()) {
2255 double avgTwoQubitGatesPerLayer = 0.0;
2256 for (const auto &layer : layers) {
2257 int twoQubitGates = 0;
2258 for (const auto &op : layer->GetOperations()) {
2259 if (op->AffectedQubits().size() >= 2) {
2260 ++twoQubitGates;
2261 }
2262 }
2263 avgTwoQubitGatesPerLayer += twoQubitGates;
2264 }
2265 avgTwoQubitGatesPerLayer /= layers.size();
2266
2267 int lookaheadVal = static_cast<int>(4. * avgTwoQubitGatesPerLayer);
2268 if (lookaheadVal > 15) lookaheadVal = 15;
2269
2270 lookaheadDepthLocal =
2271 layers.size() < 8 || nrQubits <= 10 ? 0
2272 : layers.size() < 15 ? static_cast<int>(lookaheadVal)
2273 : layers.size() < 25 ? static_cast<int>(1.5 * lookaheadVal)
2274 : 2 * lookaheadVal;
2275 }
2276
2277 int lookaheadHeuristicDepthLocal = lookaheadDepthWithHeuristic;
2278
2279 if (lookaheadHeuristicDepthLocal == std::numeric_limits<int>::max())
2280 lookaheadHeuristicDepthLocal =
2281 layers.size() < 10 || nrQubits <= 10 ? 0
2282 : layers.size() < 20 ? lookaheadDepthLocal - 1
2283 : lookaheadDepthLocal - 2;
2284
2285 if (lookaheadHeuristicDepthLocal < 0)
2286 lookaheadHeuristicDepthLocal = 0;
2287
2288 sim->setGrowthFactorGate(growthFactorGate);
2289 sim->setGrowthFactorSwap(growthFactorSwap);
2290 sim->SetUseOptimalMeetingPosition(true);
2291 sim->SetLookaheadDepth(lookaheadDepthLocal);
2292 sim->SetLookaheadDepthWithHeuristic(lookaheadHeuristicDepthLocal);
2293 sim->SetUpcomingGates(dcirc->GetOperations());
2294 }
2295 }
2296 }
2297 }
2298 }
2299
2309 auto optimiser = controller->GetOptimiser();
2310 if (optimiser) {
2311 // convert the classical state results back to the expected order
2312 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2313
2314 ConvertBackState(qubitsMap);
2315 }
2316 }
2317
2330 const std::unordered_map<Types::qubit_t, Types::qubit_t> &qubitsMap) {
2331 // might not be the one stored in the network, might exist in the DES
2332 Circuits::OperationState &theClassicalState = GetState();
2333
2334 theClassicalState.Remap(qubitsMap);
2335 }
2336
2348 auto optimiser = controller->GetOptimiser();
2349 if (optimiser) {
2350 // convert the classical state results back to the expected order
2351 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2352
2353 ConvertBackResults(res, qubitsMap);
2354 }
2355 }
2356
2370 ExecuteResults &res,
2371 const std::unordered_map<Types::qubit_t, Types::qubit_t> &bitsMap) const {
2372 ExecuteResults translatedRes;
2373
2374 size_t numClassicalBits = 0;
2375 for (const auto &[q, b] : bitsMap)
2376 if (b >= numClassicalBits) numClassicalBits = b + 1;
2377
2378 numClassicalBits = std::max(numClassicalBits, GetNumClassicalBits());
2379
2380 for (const auto &r : res) {
2381 Circuits::OperationState translatedState(r.first);
2382
2383 translatedState.Remap(bitsMap, false, numClassicalBits);
2384 translatedRes[translatedState.GetAllBits()] = r.second;
2385 }
2386
2387 res.swap(translatedRes);
2388 }
2389
2401 std::unordered_map<Types::qubit_t, Types::qubit_t> MapCircuitOnHost(
2402 const std::shared_ptr<Circuits::Circuit<Time>> &circuit, size_t hostId,
2403 size_t &nrQubits, size_t &nrCbits, bool useSeparateSimForHosts = false) {
2404 qubitsMapOnHost.clear();
2405 nrQubits = 0;
2406 nrCbits = 0;
2407 if (!circuit) return {};
2408
2409 const auto host =
2410 std::static_pointer_cast<SimpleHost<Time>>(GetHost(hostId));
2411 const size_t hostNrQubits = host->GetNumQubits();
2412
2413 std::unordered_map<Types::qubit_t, Types::qubit_t> reverseQubitsMap;
2414
2415 if (!useSeparateSimForHosts) {
2416 size_t mxq = 0;
2417 size_t mnq = std::numeric_limits<size_t>::max();
2418 size_t mxb = 0;
2419 size_t mnb = std::numeric_limits<size_t>::max();
2420
2421 for (const auto &op : circuit->GetOperations()) {
2422 const auto qbits = op->AffectedQubits();
2423 for (auto q : qbits) {
2424 if (q > mxq) mxq = q;
2425 if (q < mnq) mnq = q;
2426 }
2427 const auto cbits = op->AffectedBits();
2428 for (auto b : cbits) {
2429 if (b > mxb) mxb = b;
2430 if (b < mnb) mnb = b;
2431 }
2432 }
2433
2434 if (mnq > mxq) mnq = 0;
2435 if (mnb > mxb) mnb = 0;
2436
2437 nrQubits = mxq - mnq + 1;
2438 nrCbits = mxb - mnb + 1;
2439 if (nrCbits < nrQubits) nrCbits = nrQubits;
2440
2441 const size_t startQubit = host->GetStartQubitId();
2442
2443 if (mnq < startQubit || mxq >= startQubit + hostNrQubits) {
2444 if (nrQubits >
2445 hostNrQubits +
2446 1) // the host has an additional 'special' qubit for the
2447 // entanglement or other operations (like those for cutting)
2448 throw std::runtime_error("Circuit does not fit on the host!");
2449
2450 for (size_t i = 0; i < nrCbits; ++i) {
2451 const size_t mapFrom = mnq + i;
2452 const size_t mapTo = startQubit + i;
2453
2454 qubitsMapOnHost[mapFrom] = mapTo;
2455 reverseQubitsMap[mapTo] = mapFrom;
2456 }
2457
2458 distCirc = std::static_pointer_cast<Circuits::Circuit<Time>>(
2459 circuit->Remap(qubitsMapOnHost, qubitsMapOnHost));
2460 }
2461
2462 return reverseQubitsMap;
2463 }
2464
2465 distCirc = circuit->RemapToContinuous(qubitsMapOnHost, reverseQubitsMap,
2466 nrQubits, nrCbits);
2467
2468 assert(nrQubits == qubitsMapOnHost.size());
2469
2470 if (nrQubits == 0) nrQubits = 1;
2471
2472 if (nrQubits >
2473 hostNrQubits +
2474 1) // the host has an additional 'special' qubit for the
2475 // entanglement or other operations (like those for cutting)
2476 throw std::runtime_error("Circuit does not fit on the host!");
2477
2478 return reverseQubitsMap;
2479 }
2480
2481 bool optimizeSimulator = true;
2483
2489
2490 std::string maxBondDim;
2492 std::string mpsSample;
2494
2495 size_t maxSimulators = QC::QubitRegisterCalculator<>::
2496 GetNumberOfThreads();
2498
2501 std::shared_ptr<Simulators::ISimulator>
2503
2504 std::shared_ptr<Circuits::Circuit<Time>>
2506
2507 std::shared_ptr<IController<Time>>
2509 // TODO: depending on the network topology, we will have adiacency lists, etc.
2510 // or simply a vector of hosts for a totally connected network (or where the
2511 // communication details do not matter so much)
2512 std::vector<std::shared_ptr<IHost<Time>>>
2514
2515 std::unique_ptr<Estimators::SimulatorsEstimatorInterface<Time>>
2517
2518 private:
2520 threadsPool;
2521 bool recreateIfNeeded =
2522 true;
2523 std::unordered_map<Types::qubit_t, Types::qubit_t>
2524 qubitsMapOnHost;
2527 const std::vector<std::string> *pauliStrings =
2528 nullptr;
2530
2531 bool optimizeInitialQubitsMap = true;
2533 bool mpsOptimizeSwaps = true;
2534 size_t mpsOptimizationBondDimensionThreshold =
2535 32;
2536 size_t mpsOptimizationQubitsNumberThreshold =
2537 12;
2538
2539 int lookaheadDepth =
2540 std::numeric_limits<int>::max();
2542 int lookaheadDepthWithHeuristic = std::numeric_limits<int>::max();
2544
2545 double growthFactorSwap = 1.;
2546 double growthFactorGate = 0.7;
2547};
2548
2549} // namespace Network
2550
2551#endif // !_SIMPLE_NETWORK_H_
int GetSimulationType(void *sim)
Circuit class for holding the sequence of operations.
Definition Circuit.h:46
static std::shared_ptr< Circuits::Circuit< Time > > LayersToCircuit(const std::vector< std::shared_ptr< Circuits::Circuit< Time > > > &layers)
Converts the layers back to a circuit.
Definition Circuit.h:2417
The operation interface.
Definition Operations.h:358
The state class that stores the classical state of a quantum circuit execution.
Definition Operations.h:63
const std::vector< bool > & GetAllBits() const
Get the classical bits.
Definition Operations.h:214
void Clear()
Clear the classical state.
Definition Operations.h:169
void SetResultsInOrder(const std::vector< bool > &results)
Set the classical bits.
Definition Operations.h:253
void Remap(const std::unordered_map< Types::qubit_t, Types::qubit_t > &mapping, bool ignoreNotMapped=false, size_t newSize=0)
Convert the state using the provided mapping.
Definition Operations.h:297
static void ExecuteUpToMeasurements(const std::shared_ptr< Circuits::Circuit< Time > > &dcirc, size_t nrQubits, size_t nrCbits, size_t nrResultCbits, const std::shared_ptr< Simulators::ISimulator > &sim, std::vector< bool > &executed)
The controller host interface.
Definition Controller.h:106
The network interface.
Definition Network.h:58
std::shared_ptr< INetwork< Time > > getptr()
Definition Network.h:772
typename Circuits::Circuit< Time >::ExecuteResults ExecuteResults
Definition Network.h:60
std::unordered_set< SimulatorPair, boost::hash< SimulatorPair > > SimulatorsSet
Definition Network.h:63
Simulators::SimulatorType GetLastSimulatorType() const override
Get the last used simulator type.
void SetController(const std::shared_ptr< IController< Time > > &cntrl)
Set the network controller host.
std::vector< double > ExecuteExpectations(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, const std::vector< std::string > &paulis) override
Execute the circuit on the network and return the expectation values for the specified Pauli strings.
Simulators::SimulatorType lastSimulatorType
The last simulator type used.
size_t GetHostIdForEntangledQubit(size_t qubitId) const override
Get the host id for the specified qubit used for entanglement between hosts.
std::shared_ptr< Simulators::ISimulator > simulator
The quantum computing simulator for the network.
bool ExpectsClassicalBitFromOtherHost(const std::shared_ptr< Circuits::IOperation< Time > > &op) const override
Checks if a gate expects a classical bit from another host.
void ExecuteOnHost(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t hostId) override
Execute the circuit on the specified host.
size_t GetHostIdForClassicalControl(const std::shared_ptr< Circuits::IOperation< Time > > &op) const override
Get the host id where the classical control bit resides for a conditioned gate.
typename BaseClass::ExecuteResults ExecuteResults
The execute results type.
void MarkEntangledQubitFree(size_t qubitId) override
Mark the specified qubit used for entanglement between hosts as free.
bool OptimizationSimulatorExists(Simulators::SimulatorType type, Simulators::SimulationType kind) const override
Checks if a simulator exists in the optimization set.
size_t GetNumNetworkEntangledQubits() const override
Get the number of qubits used for entanglement between hosts.
std::unordered_map< Types::qubit_t, Types::qubit_t > MapCircuitOnHost(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t hostId, size_t &nrQubits, size_t &nrCbits, bool useSeparateSimForHosts=false)
Map the circuit on the host.
size_t GetNumQubitsForHost(size_t hostId) const override
Get the number of qubits in the network for the specified host.
Circuits::OperationState & GetState() override
Get the classical state of the network.
void AddOptimizationSimulator(Simulators::SimulatorType type, Simulators::SimulationType kind) override
Adds a simulator to the simulators optimization set.
ExecuteResults RepeatedExecuteOnHost(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t hostId, size_t shots=1000) override
Execute the circuit on the specified host, repeatedly.
void OptimizeMPSInitialQubitsMap(std::shared_ptr< Simulators::ISimulator > &sim, std::shared_ptr< Circuits::Circuit< Time > > &dcirc, size_t nrQubits) const
Simulators::SimulationType lastMethod
The last simulation method used.
ExecuteResults RepeatedExecute(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t shots=1000) override
Execute the circuit on the network, repeatedly.
bool optimizeSimulator
The flag to optimize the simulator.
std::vector< size_t > GetQubitsIds(size_t hostId) const override
Get the qubit ids for the specified host.
std::shared_ptr< Circuits::Circuit< Time > > GetDistributedCircuit() const override
Get the distributed circuit.
size_t GetNumQubits() const override
Get the number of qubits in the network.
bool GetOptimizeSimulator() const override
Returns the 'optimize' flag.
size_t GetNumberOfGatesDistributedOrCut(const std::shared_ptr< Circuits::Circuit< Time > > &circuit) const override
Get the number of gates that span more than one host.
size_t GetHostIdForClassicalBit(size_t classicalBitId) const override
Get the host id for the specified classical bit.
bool IsLocalOperation(const std::shared_ptr< Circuits::IOperation< Time > > &op) const override
Check if the circuit operation is local.
void SetLookaheadDepthWithHeuristic(int depth) override
void Configure(const char *key, const char *value) override
Configures the network.
void CreateSimulator(Simulators::SimulatorType simType=Simulators::SimulatorType::kQCSim, Simulators::SimulationType simExecType=Simulators::SimulationType::kMatrixProductState, size_t nrQubits=0) override
Create the simulator for the network.
std::unique_ptr< Estimators::SimulatorsEstimatorInterface< Time > > simulatorsEstimator
The simulators estimator.
const std::shared_ptr< IHost< Time > > GetHost(size_t hostId) const override
Get the host with the specified id.
size_t GetNumHosts() const override
Get the number of hosts in the network.
void SetMPSOptimizeSwaps(bool optimize=true) override
void SetInitialQubitsMapOptimization(bool optimize=true) override
size_t GetMPSOptimizationBondDimensionThreshold() const override
size_t GetHostIdForQubit(size_t qubitId) const override
Get the host id for the specified qubit.
bool IsNetworkEntangledQubit(size_t qubitId) const override
Check if the specified qubit id is for a qubit used for entanglement between hosts.
std::vector< double > ExecuteOnHostExpectations(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t hostId, const std::vector< std::string > &paulis) override
Execute the circuit on the specified host and return the expectation values for the specified Pauli s...
void RemoveOptimizationSimulator(Simulators::SimulatorType type, Simulators::SimulationType kind) override
Removes a simulator from the simulators optimization set.
std::shared_ptr< INetwork< Time > > Clone() const override
Clone the network.
std::complex< double > ExecuteOnHostProjectOnZero(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t hostId) override
Execute circuit on host and return the projection onto the zero state.
Simulators::SimulationType GetLastSimulationType() const override
Get the last used simulation type.
void SetMaxSimulators(size_t val) override
Set the maximum number of simulators that can be used in the network.
bool AreQubitAndClassicalBitOnSameHost(size_t qubitId, size_t bitId) const override
Check if the specified qubit and classical bit are on the same host.
void SetOptimizeSimulator(bool optimize=true) override
Allows using an optimized simulator.
size_t GetNumClassicalBitsForHost(size_t hostId) const override
Get the number of classical bits in the network for the specified host.
bool IsEntanglingGate(const std::shared_ptr< Circuits::IOperation< Time > > &op) const override
Checks if a gate is an entangling gate.
void Execute(const std::shared_ptr< Circuits::Circuit< Time > > &circuit) override
Execute the circuit on the network.
std::vector< std::shared_ptr< IHost< Time > > > hosts
The hosts in the network.
bool AreQubitsOnSameHost(size_t qubitId1, size_t qubitId2) const override
Check if the specified qubits are on the same host.
std::vector< ExecuteResults > ExecuteScheduled(const std::vector< Schedulers::ExecuteCircuit< Time > > &circuits) override
Schedule and execute circuits on the network.
size_t GetHostIdForAnyQubit(size_t qubitId) const override
Get the host id for the specified qubit.
size_t GetNumNetworkEntangledQubitsForHost(size_t hostId) const override
Get the number of qubits used for entanglement between hosts for the specified host.
Circuits::OperationState classicalState
The classical state of the network.
void ConvertBackResults(ExecuteResults &res, const std::unordered_map< Types::qubit_t, Types::qubit_t > &bitsMap) const
Converts back the results using the passed qubits map.
void ConvertBackState()
Converts back the state from the optimized network distribution mapping.
std::shared_ptr< Circuits::Circuit< Time > > distCirc
The distributed circuit.
std::shared_ptr< Schedulers::IScheduler< Time > > GetScheduler() const override
Get the scheduler for the network.
size_t GetNumClassicalBits() const override
Get the number of classical bits in the network.
const std::shared_ptr< IController< Time > > GetController() const override
Get the controller for the network.
bool IsDistributedOperation(const std::shared_ptr< Circuits::IOperation< Time > > &op) const override
Check if the circuit operation is distributed.
std::vector< std::shared_ptr< IHost< Time > > > & GetHosts()
Get the hosts in the network.
bool SendPacket(size_t fromHostId, size_t toHostId, const std::vector< uint8_t > &packet) override
Sends a packet between two hosts.
const BaseClass::SimulatorsSet & GetSimulatorsSet() const override
Get the optimizations simulators set.
std::shared_ptr< Simulators::ISimulator > ChooseBestSimulator(std::shared_ptr< Circuits::Circuit< Time > > &dcirc, size_t &counts, size_t nrQubits, size_t nrCbits, size_t nrResultCbits, Simulators::SimulatorType &simType, Simulators::SimulationType &method, std::vector< bool > &executed, bool multithreading=false, bool dontRunCircuitStart=false) const override
std::shared_ptr< Simulators::ISimulator > GetSimulator() const override
Get the simulator for the network.
bool OperatesWithNetworkEntangledQubit(const std::shared_ptr< Circuits::IOperation< Time > > &op) const override
Check if the circuit operation operates on the entanglement qubits between hosts.
std::vector< std::complex< double > > ExecuteOnHostAmplitudes(const std::shared_ptr< Circuits::Circuit< Time > > &circuit, size_t hostId) override
Execute circuit on host and return full statevector amplitudes.
size_t GetMaxSimulators() const override
Get the maximum number of simulators that can be used in the network.
void MarkEntangledQubitsBusy(size_t qubitId1, size_t qubitId2) override
Mark the pair of the specified qubits used for entanglement between hosts as busy.
void CreateScheduler(SchedulerType schType=SchedulerType::kNoEntanglementQubitsParallel) override
Create the scheduler for the network.
void setGrowthFactorSwap(double factor) override
std::vector< size_t > GetClassicalBitsIds(size_t hostId) const override
Get the classical bit ids for the specified host.
std::vector< size_t > GetEntangledQubitMeasurementBitIds(size_t hostId) const override
Get the classical bit ids used for measurement of entanglement qubits between the hosts for the speci...
bool AreEntanglementQubitsBusy(size_t qubitId1, size_t qubitId2) const override
Check if any of the two specified qubits used for entanglement between hosts are busy.
INetwork< Time > BaseClass
The base class type.
size_t maxSimulators
The maximum number of simulators that can be used in the network.
std::shared_ptr< IController< Time > > controller
The controller for the network.
void ClearEntanglements() override
Clear all entanglements between hosts in the network.
void ConvertBackResults(ExecuteResults &res)
Converts back the results from the optimized network distribution mapping.
void SetMPSOptimizationQubitsNumberThreshold(size_t threshold) override
bool IsEntanglementQubitBusy(size_t qubitId) const override
Check if the specified qubit used for entanglement between hosts is busy.
void RemoveAllOptimizationSimulatorsAndAdd(Simulators::SimulatorType type, Simulators::SimulationType kind) override
Removes all simulators from the simulators optimization set and adds the one specified.
void CreateNetwork(const std::vector< Types::qubit_t > &qubits, const std::vector< size_t > &cbits)
Creates the network hosts and controller.
SimpleDisconnectedNetwork(const std::vector< Types::qubit_t > &qubits={}, const std::vector< size_t > &cbits={})
The constructor.
std::vector< size_t > GetNetworkEntangledQubitsIds(size_t hostId) const override
Get the qubit ids used for entanglement between hosts for the specified host.
void ConvertBackState(const std::unordered_map< Types::qubit_t, Types::qubit_t > &qubitsMap)
Converts back the state using the passed qubits map.
void setGrowthFactorGate(double factor) override
NetworkType GetType() const override
Get the type of the network.
void SetMPSOptimizationBondDimensionThreshold(size_t threshold) override
bool AreClassicalBitsOnSameHost(size_t bitId1, size_t bitId2) const override
Check if the specified classical bits are on the same host.
size_t GetMPSOptimizationQubitsNumberThreshold() const override
The simple host implementation.
Definition SimpleHost.h:44
void setGrowthFactorSwap(double factor)
std::vector< long long int > ComputeOptimalQubitsMap(const std::vector< std::shared_ptr< Circuits::Circuit<> > > &layers, int nrShuffles=0, int nrSwaps=0)
void setGrowthFactorGate(double factor)
void SetMaxBondDimension(IndexType val)
static std::shared_ptr< ISimulator > CreateSimulator(SimulatorType t=SimulatorType::kQCSim, SimulationType method=SimulationType::kMatrixProductState)
Create a quantum computing simulator.
Definition Factory.cpp:101
ThreadsPool class for holding and controlling a pool of threads.
Definition ThreadsPool.h:39
@ kGate
the usual quantum gate, result stays in simulator's state
Definition Operations.h:28
NetworkType
The type of the network.
Definition Network.h:34
@ kSimpleDisconnectedNetwork
Simple network, no communication among hosts, sequential simulation.
Definition Network.h:35
SchedulerType
The type of the network scheduler for scheduling execution of multiple circuits.
Definition Controller.h:86
SimulationType
The type of simulation.
Definition State.h:85
@ kStatevector
statevector simulation type
Definition State.h:86
@ kMatrixProductState
matrix product state simulation type
Definition State.h:87
@ kStabilizer
Clifford gates simulation type.
Definition State.h:88
@ kPauliPropagator
Pauli propagator simulation type.
Definition State.h:90
@ kTensorNetwork
Tensor network simulation type.
Definition State.h:89
@ kPathIntegral
Path integral simulation type.
Definition State.h:92
SimulatorType
The type of simulator.
Definition State.h:68
@ kCompositeQCSim
composite qcsim simulator type
Definition State.h:76
@ kQCSim
qcsim simulator type
Definition State.h:72
@ kQiskitAer
qiskit aer simulator type
Definition State.h:70
@ kQuestSim
quest simulator type
Definition State.h:78
@ kCompositeQiskitAer
composite qiskit aer simulator type
Definition State.h:74
@ kGpuSim
gpu simulator type
Definition State.h:77
double time_type
The type of time.
Definition Types.h:24
A way to pack together a circuit and the number of shots for its execution.
Definition Controller.h:61