Maestro 0.2.5
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
SimpleDisconnectedNetwork.h
Go to the documentation of this file.
1
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"
21
22#include "../Estimators/SimulatorsEstimatorInterface.h"
23#include "NetworkJob.h"
24
25namespace Network {
26
40template <typename Time = Types::time_type,
41 class Controller = SimpleController<Time>>
42class SimpleDisconnectedNetwork : public INetwork<Time> {
43 public:
57 SimpleDisconnectedNetwork(const std::vector<Types::qubit_t> &qubits = {},
58 const std::vector<size_t> &cbits = {}) {
59 if (!qubits.empty()) CreateNetwork(qubits, cbits);
60 }
61
71 void CreateNetwork(const std::vector<Types::qubit_t> &qubits,
72 const std::vector<size_t> &cbits) {
73 size_t qubitsOffset = 0;
74 size_t cbitsOffset = 0;
75
76 for (size_t i = 0; i < qubits.size(); ++i) {
77 const size_t numQubits = qubits[i];
78 const size_t numBits = (i < cbits.size() ? cbits[i] : 0);
79 hosts.emplace_back(std::make_shared<SimpleHost<Time>>(
80 i, qubitsOffset, numQubits, cbitsOffset, numBits));
81 qubitsOffset += numQubits;
82 cbitsOffset += numBits;
83 }
84
85 for (size_t i = 0; i < hosts.size(); ++i) {
86 std::static_pointer_cast<SimpleHost<Time>>(hosts[i])->SetEntangledQubitId(
87 qubitsOffset);
88 std::static_pointer_cast<SimpleHost<Time>>(hosts[i])
89 ->SetEntangledQubitMeasurementBit(cbitsOffset);
90 ++qubitsOffset;
91 ++cbitsOffset;
92 }
93
94 controller = std::make_shared<Controller>();
95 }
96
108 const std::shared_ptr<Circuits::Circuit<Time>> &circuit) override {
109 const auto recreate = recreateIfNeeded;
110
113 size_t numQubits = 2;
114 if (simulator) {
115 simType = simulator->GetType();
116 method = simulator->GetSimulationType();
117 numQubits = simulator->GetNumberOfQubits();
118 }
119
120 recreateIfNeeded = false;
121
122 const auto res = RepeatedExecute(circuit, 1);
123
124 recreateIfNeeded = recreate;
125
126 // put the results in the state
127 if (!res.empty()) {
128 const auto &first = *res.begin();
129 GetState().SetResultsInOrder(first.first);
130 }
131
132 if (recreate &&
133 (!simulator ||
134 (simulator && (simType != simulator->GetType() ||
135 method != simulator->GetSimulationType() ||
136 simulator->GetNumberOfQubits() != numQubits))))
137 CreateSimulator(simType, method);
138 }
139
152 void ExecuteOnHost(const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
153 size_t hostId) override {
154 const auto recreate = recreateIfNeeded;
155
158 size_t numQubits = 2;
159 if (simulator) {
160 simType = simulator->GetType();
161 method = simulator->GetSimulationType();
162 numQubits = simulator->GetNumberOfQubits();
163 }
164
165 recreateIfNeeded = false;
166
167 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
168
169 recreateIfNeeded = recreate;
170
171 // put the results in the state
172 if (!res.empty()) {
173 const auto &first = *res.begin();
174 GetState().SetResultsInOrder(first.first);
175 }
176
177 if (recreate &&
178 (!simulator ||
179 (simulator && (simType != simulator->GetType() ||
180 method != simulator->GetSimulationType() ||
181 simulator->GetNumberOfQubits() != numQubits))))
182 CreateSimulator(simType, method);
183 }
184
200 std::vector<double> ExecuteExpectations(
201 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
202 const std::vector<std::string> &paulis) override {
203 const auto recreate = recreateIfNeeded;
204
207 size_t numQubits = 2;
208 if (simulator) {
209 simType = simulator->GetType();
210 method = simulator->GetSimulationType();
211 numQubits = simulator->GetNumberOfQubits();
212 }
213
214 recreateIfNeeded = false;
215
216 pauliStrings = &paulis;
217 const auto res = RepeatedExecute(circuit, 1);
218 pauliStrings = nullptr;
219
220 recreateIfNeeded = recreate;
221
222 // put the results in the state
223 if (!res.empty()) {
224 const auto &first = *res.begin();
225 GetState().SetResultsInOrder(first.first);
226 }
227
228 std::vector<double> expectations(paulis.size());
229 if (simulator) {
230 // translate the pauli strings to the mapped order of qubits
231 const size_t numOps = simulator->GetNumberOfQubits();
232
233 auto optimiser = controller->GetOptimiser();
234 if (optimiser) {
235 // convert the classical state results back to the expected order
236 const auto &qubitsMap = optimiser->GetQubitsMap();
237
238 for (size_t i = 0; i < paulis.size(); ++i) {
239 std::string translated(numOps, 'I');
240
241 for (size_t j = 0; j < paulis[i].size(); ++j) {
242 const auto pos = qubitsMap.find(j);
243 if (pos != qubitsMap.end())
244 translated[pos->second] = paulis[i][j];
245 else
246 translated[j] = paulis[i][j];
247 }
248
249 expectations[i] = simulator->ExpectationValue(translated);
250 }
251 } else {
252 for (size_t i = 0; i < paulis.size(); ++i)
253 expectations[i] = simulator->ExpectationValue(paulis[i]);
254 }
255 }
256
257 if (recreate && (!simulator || simType != simulator->GetType() ||
258 method != simulator->GetSimulationType() ||
259 simulator->GetNumberOfQubits() != numQubits))
260 CreateSimulator(simType, method);
261
262 return expectations;
263 }
264
280 std::vector<double> ExecuteOnHostExpectations(
281 const std::shared_ptr<Circuits::Circuit<Time>> &circuit, size_t hostId,
282 const std::vector<std::string> &paulis) override {
283 const auto recreate = recreateIfNeeded;
284
287 size_t numQubits = 2;
288 if (simulator) {
289 simType = simulator->GetType();
290 method = simulator->GetSimulationType();
291 numQubits = simulator->GetNumberOfQubits();
292 }
293
294 // RAII: restore recreateIfNeeded and clear pauliStrings on any exit path.
295 struct ScopedRestore {
296 bool &flag, saved;
297 const std::vector<std::string> **ps;
298 ScopedRestore(bool &f, const std::vector<std::string> **p)
299 : flag(f), saved(f), ps(p) {
300 flag = false;
301 }
302 ~ScopedRestore() {
303 flag = saved;
304 *ps = nullptr;
305 }
306 } restoreGuard(recreateIfNeeded, &pauliStrings);
307
308 pauliStrings = &paulis;
309 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
310
311 // put the results in the state
312 if (!res.empty()) {
313 const auto &first = *res.begin();
314 GetState().SetResultsInOrder(first.first);
315 }
316
317 // for (const auto& m : qubitsMapOnHost)
318 // std::cout << "Mapping qubit " << m.first << " to " << m.second <<
319 // std::endl;
320
321 const size_t offsetBase = qubitsMapOnHost.size();
322
323 std::vector<double> expectations(paulis.size(), 1.);
324 if (simulator) {
325 // translate the pauli strings to the mapped order of qubits
326 const size_t numOps = simulator->GetNumberOfQubits();
327
328 // convert the pauli strings to the actual qubits order
329 for (size_t i = 0; i < paulis.size(); ++i) {
330 std::string translated(std::max(numOps, paulis[i].size()), 'I');
331
332 size_t offset = offsetBase;
333
334 for (size_t j = 0; j < paulis[i].size(); ++j) {
335 auto pos = qubitsMapOnHost.find(j);
336 if (pos != qubitsMapOnHost.end())
337 translated[pos->second] = paulis[i][j];
338 else {
339 translated[offset] = paulis[i][j];
340 ++offset;
341 }
342 }
343
344 // std::cout << "Translated pauli string: " << translated << std::endl;
345
346 expectations[i] = simulator->ExpectationValue(translated);
347 }
348 } else {
349 throw std::runtime_error(
350 "ExecuteOnHostExpectations: no simulator available after execution.");
351 }
352
353 if (recreate && (!simulator || simType != simulator->GetType() ||
354 method != simulator->GetSimulationType() ||
355 simulator->GetNumberOfQubits() != numQubits))
356 CreateSimulator(simType, method);
357
358 return expectations;
359 }
360
372 std::vector<std::complex<double>> ExecuteOnHostAmplitudes(
373 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
374 size_t hostId) override {
375 const auto recreate = recreateIfNeeded;
376
379 size_t numQubits = 2;
380 if (simulator) {
381 simType = simulator->GetType();
382 method = simulator->GetSimulationType();
383 numQubits = simulator->GetNumberOfQubits();
384 }
385
386 // RAII: restore recreateIfNeeded on any exit path (including exceptions).
387 struct ScopedRestoreFlag {
388 bool &flag, saved;
389 ScopedRestoreFlag(bool &f) : flag(f), saved(f) { flag = false; }
390 ~ScopedRestoreFlag() { flag = saved; }
391 } restoreGuard(recreateIfNeeded);
392
393 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
394
395 if (!res.empty()) {
396 const auto &first = *res.begin();
397 GetState().SetResultsInOrder(first.first);
398 }
399
400 if (!simulator)
401 throw std::runtime_error(
402 "ExecuteOnHostAmplitudes: no simulator available after execution.");
403
404 std::vector<std::complex<double>> amplitudes;
405 const size_t n = simulator->GetNumberOfQubits();
406 const size_t dim = 1ULL << n;
407 amplitudes.resize(dim);
408 for (size_t state = 0; state < dim; ++state) amplitudes[state] = simulator->Amplitude(state);
409
410 // Remap amplitudes back to the original qubit ordering if qubits were
411 // remapped during execution on the host.
412 if (!qubitsMapOnHost.empty()) {
413 const size_t offsetBase = qubitsMapOnHost.size();
414
415 // Build reverse mapping: simulator qubit position -> original qubit
416 // position.
417 std::vector<size_t> simToOrig(n);
418 size_t offset = offsetBase;
419
420 for (size_t qbit = 0; qbit < n; ++qbit)
421 {
422 auto pos = qubitsMapOnHost.find(qbit);
423 if (pos != qubitsMapOnHost.end())
424 simToOrig[pos->second] = pos->first;
425 else
426 simToOrig[qbit] = offset++;
427 }
428
429 std::vector<std::complex<double>> remapped(dim);
430
431 for (size_t sim_state = 0; sim_state < dim; ++sim_state) {
432 size_t orig_state = 0;
433 for (size_t qbit = 0; qbit < n; ++qbit) {
434 if (sim_state & (1ULL << qbit))
435 orig_state |= (1ULL << simToOrig[qbit]);
436 }
437 if (orig_state < dim) remapped[orig_state] = amplitudes[sim_state];
438 }
439 amplitudes.swap(remapped);
440 }
441
442 if (recreate && (!simulator || simType != simulator->GetType() ||
443 method != simulator->GetSimulationType() ||
444 simulator->GetNumberOfQubits() != numQubits))
445 CreateSimulator(simType, method);
446
447 return amplitudes;
448 }
449
463 std::complex<double> ExecuteOnHostProjectOnZero(
464 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
465 size_t hostId) override {
466 const auto recreate = recreateIfNeeded;
467
470 size_t numQubits = 2;
471 if (simulator) {
472 simType = simulator->GetType();
473 method = simulator->GetSimulationType();
474 numQubits = simulator->GetNumberOfQubits();
475 }
476
477 // RAII: restore recreateIfNeeded on any exit path (including exceptions).
478 struct ScopedRestoreFlag {
479 bool &flag, saved;
480 ScopedRestoreFlag(bool &f) : flag(f), saved(f) { flag = false; }
481 ~ScopedRestoreFlag() { flag = saved; }
482 } restoreGuard(recreateIfNeeded);
483
484 const auto res = RepeatedExecuteOnHost(circuit, hostId, 1);
485
486 if (!res.empty()) {
487 const auto &first = *res.begin();
488 GetState().SetResultsInOrder(first.first);
489 }
490
491 if (!simulator)
492 throw std::runtime_error(
493 "ExecuteOnHostProjectOnZero: no simulator available after "
494 "execution.");
495
496 const std::complex<double> result = simulator->ProjectOnZero();
497
498 if (recreate && (!simulator || simType != simulator->GetType() ||
499 method != simulator->GetSimulationType() ||
500 simulator->GetNumberOfQubits() != numQubits))
501 CreateSimulator(simType, method);
502
503 return result;
504 }
505
520 const std::shared_ptr<Circuits::Circuit<Time>> &circuit,
521 size_t shots = 1000) override {
522 if (!controller || !circuit) return {};
523
524 distCirc = controller->DistributeCircuit(BaseClass::getptr(), circuit);
525 if (!distCirc) return {};
526
527#ifdef _DEBUG
528 for (auto q : distCirc->AffectedQubits()) {
529 if (q >= GetNumQubits()) {
530 std::cout
531 << "This is a distributed circuit, using entanglement or cutting"
532 << std::endl;
533 break;
534 }
535 }
536#endif
537
538 if (!simulator) return {};
539
540 auto simType = simulator->GetType();
541 if (distCirc->HasOpsAfterMeasurements() &&
542 (
543#ifndef NO_QISKIT_AER
545#endif
547 distCirc->MoveMeasurementsAndResets();
548
549 auto method = simulator->GetSimulationType();
550
551 const auto saveSimType = simType;
552 const auto saveMethod = method;
553
554 if (GetOptimizeSimulator() && distCirc->IsClifford() &&
556 // this is for the gpu simulator, as it doesn't support stabilizer
557#ifdef __linux__
559#endif
560 ) {
562
565#ifndef NO_QISKIT_AER
568#endif
569 }
570
571 ExecuteResults res;
572 const size_t nrQubits = GetNumQubits() + GetNumNetworkEntangledQubits();
573 const size_t nrCbitsResults = GetNumClassicalBits();
574
575 maxBondDim =
576 simulator->GetConfiguration("matrix_product_state_max_bond_dimension");
577 singularValueThreshold = simulator->GetConfiguration(
578 "matrix_product_state_truncation_threshold");
579 mpsSample = simulator->GetConfiguration("mps_sample_measure_algorithm");
580
581 // do that only if the optimization for simulator is on and the estimator is
582 // available, ortherwise an 'optimal' simulator won't be created
584 simulatorsEstimator->IsInitialized()) {
585 simulator->Clear();
586 GetState().Clear();
587 }
588
589 std::vector<bool> executed;
590 auto optSim =
591 ChooseBestSimulator(distCirc, shots, nrQubits, nrQubits, nrCbitsResults,
592 simType, method, executed);
593
594 lastSimulatorType = simType;
595 lastMethod = method;
596
597 size_t nrThreads = GetMaxSimulators();
598
599#ifdef __linux__
601 nrThreads = 1;
602 else
603#endif
605 !distCirc->HasOpsAfterMeasurements()) ||
607 nrThreads = 1;
608
609 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
610
611 std::mutex resultsMutex;
612
613 const auto dcirc = distCirc;
614
615 if (nrThreads > 1) {
616 // since it's going to execute on multiple threads, free the memory from
617 // the network's simulator and state, it's going to use other ones,
618 // created in the threads if optimization already exists, it will be
619 // cloned in the threads, otherwise a new one will be created in the
620 // threads
623 ->IsInitialized()) // otherwise it was already cleared
624 {
625 simulator->Clear();
626 GetState().Clear();
627 }
628
629 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
630
631 threadsPool.Resize(nrThreads);
632 threadsPool.SetFinishLimit(shots);
633
634 while (shots > 0) {
635 const size_t curCnt = std::min(cntPerThread, shots);
636
637 shots -= curCnt;
638
639 auto job = std::make_shared<ExecuteJob<Time>>(
640 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
641 method, resultsMutex);
642 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
643
644 job->maxBondDim = maxBondDim;
645 job->mpsSample = mpsSample;
646 job->singularValueThreshold = singularValueThreshold;
647
648 if (optSim) {
649 job->optSim = optSim->Clone();
650 job->executedGates = executed;
651 }
652
653 threadsPool.AddRunJob(std::move(job));
654 }
655
656 threadsPool.WaitForFinish();
657 threadsPool.Stop();
658 } else {
659 const size_t curCnt = shots;
660
661 auto job = std::make_shared<ExecuteJob<Time>>(
662 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
663 method, resultsMutex);
664 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
665
666 job->maxBondDim = maxBondDim;
667 job->mpsSample = mpsSample;
668 job->singularValueThreshold = singularValueThreshold;
669
670 if (optSim) {
671 optSim->SetMultithreading(true);
672 job->optSim = optSim;
673 job->executedGates = executed;
674 } else {
675 if (simulator && method == saveMethod && simType == saveSimType) {
676 // use the already created simulator
677 optSim = simulator;
678 job->optSim = optSim;
679 job->executedGates.resize(distCirc->size(),
680 false); // no gates executed yet
681 simulator = nullptr;
682 }
683 }
684
685 job->DoWorkNoLock();
686 if (!recreateIfNeeded) simulator = job->optSim;
687 }
688
689 if (recreateIfNeeded) CreateSimulator(saveSimType, saveMethod);
690
692
693 return res;
694 }
695
712 const std::shared_ptr<Circuits::Circuit<Time>> &circuit, size_t hostId,
713 size_t shots = 1000) override {
714 if (!circuit || hostId >= GetNumHosts()) return {};
715
716 size_t nrQubits = 0;
717 size_t nrCbits = 0;
718
719 std::shared_ptr<Circuits::Circuit<Time>> optCircuit;
720 if (GetController()->GetOptimizeCircuit()) {
721 optCircuit =
722 std::static_pointer_cast<Circuits::Circuit<Time>>(circuit->Clone());
723 optCircuit->Optimize();
724 }
725 const auto reverseQubitsMap = MapCircuitOnHost(
726 GetController()->GetOptimizeCircuit() ? optCircuit : circuit, hostId,
727 nrQubits, nrCbits, true);
728 if (nrCbits == 0) nrCbits = nrQubits;
729
730 if (!simulator || !distCirc) return {};
731
732 auto simType = simulator->GetType();
733
734 maxBondDim =
735 simulator->GetConfiguration("matrix_product_state_max_bond_dimension");
736 singularValueThreshold = simulator->GetConfiguration(
737 "matrix_product_state_truncation_threshold");
738 mpsSample = simulator->GetConfiguration("mps_sample_measure_algorithm");
739
740 if (distCirc->HasOpsAfterMeasurements() &&
741 (
742#ifndef NO_QISKIT_AER
744#endif
746 distCirc->MoveMeasurementsAndResets();
747
748 auto method = simulator->GetSimulationType();
749 const auto saveSimType = simType;
750 const auto saveMethod = method;
751
752 if (GetOptimizeSimulator() && distCirc->IsClifford() &&
754 // this is for the gpu simulator, as it doesn't support stabilizer
755#ifdef __linux__
757#endif
758 ) {
760
763#ifndef NO_QISKIT_AER
766#endif
767 }
768
769 ExecuteResults res;
770
771 // since it's going to execute on multiple threads, free the memory from the
772 // network's simulator and state, it's going to use other ones, created in
773 // the threads
774 simulator->Clear();
775 GetState().Clear();
776
777 std::vector<bool> executed;
778 auto optSim = ChooseBestSimulator(distCirc, shots, nrQubits, nrCbits,
779 nrCbits, simType, method, executed);
780
781 lastSimulatorType = simType;
782 lastMethod = method;
783
784 size_t nrThreads = GetMaxSimulators();
785
786#ifdef __linux__
788 nrThreads = 1;
789 else
790#endif
792 !distCirc->HasOpsAfterMeasurements()) ||
794 nrThreads = 1;
795
796 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
797
798 // WARNING: be sure to not put this above ChooseBestSimulator, as that one
799 // can change the shots value!
800
801 std::mutex resultsMutex;
802
803 const auto dcirc = distCirc;
804
805 if (nrThreads > 1) {
806 // this rounds up, rounding down is better
807 // const size_t cntPerThread = static_cast<size_t>((shots - 1) / nrThreads
808 // + 1);
809 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
810
811 threadsPool.Resize(nrThreads);
812 threadsPool.SetFinishLimit(shots);
813
814 while (shots > 0) {
815 const size_t curCnt = std::min(cntPerThread, shots);
816 shots -= curCnt;
817
818 auto job = std::make_shared<ExecuteJob<Time>>(
819 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
820 resultsMutex);
821 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
822
823 job->maxBondDim = maxBondDim;
824 job->mpsSample = mpsSample;
825 job->singularValueThreshold = singularValueThreshold;
826
827 if (optSim) {
828 job->optSim = optSim->Clone();
829 job->executedGates = executed;
830 }
831
832 threadsPool.AddRunJob(std::move(job));
833 }
834
835 threadsPool.WaitForFinish();
836 threadsPool.Stop();
837 } else {
838 const size_t curCnt = shots;
839
840 auto job = std::make_shared<ExecuteJob<Time>>(
841 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
842 resultsMutex);
843 job->optimiseMultipleShotsExecution = GetOptimizeSimulator();
844
845 job->maxBondDim = maxBondDim;
846 job->mpsSample = mpsSample;
847 job->singularValueThreshold = singularValueThreshold;
848
849 if (optSim) {
850 optSim->SetMultithreading(true);
851 job->optSim = optSim;
852 job->executedGates = executed;
853 }
854
855 job->DoWorkNoLock();
856 if (!recreateIfNeeded) simulator = job->optSim;
857 }
858
859 if (recreateIfNeeded) CreateSimulator(saveSimType, saveMethod);
860
861 if (!reverseQubitsMap.empty()) ConvertBackResults(res, reverseQubitsMap);
862
863 return res;
864 }
865
875 const std::shared_ptr<Circuits::Circuit<Time>> &circuit) const override {
876 if (!circuit) return 0;
877
878 size_t distgates = 0;
879
880 for (const auto &op : circuit->GetOperations())
881 if (!IsLocalOperation(op)) ++distgates;
882
883 return distgates;
884 }
885
902 std::vector<ExecuteResults> ExecuteScheduled(
903 const std::vector<Schedulers::ExecuteCircuit<Time>> &circuits) override {
904 // create a default one if not set
905 if (!GetScheduler()) {
907
908 if (!GetScheduler()) return {};
909 }
910
911 return GetScheduler()->ExecuteScheduled(circuits);
912 }
913
935 Simulators::SimulationType simExecType =
937 size_t nrQubits = 0) override {
941
942 simulator =
944
945 if (simulator) {
946 if (!maxBondDim.empty())
947 simulator->Configure("matrix_product_state_max_bond_dimension",
948 maxBondDim.c_str());
949 if (!singularValueThreshold.empty())
950 simulator->Configure("matrix_product_state_truncation_threshold",
951 singularValueThreshold.c_str());
952 if (!mpsSample.empty())
953 simulator->Configure("mps_sample_measure_algorithm", mpsSample.c_str());
954 if (useDoublePrecision) simulator->Configure("use_double_precision", "1");
955
956 simulator->AllocateQubits(
957 nrQubits == 0 ? GetNumQubits() + GetNumNetworkEntangledQubits()
958 : nrQubits);
959 simulator->Initialize();
960 }
961 }
962
972 void Configure(const char *key, const char *value) override {
973 if (!key || !value) return;
974
975 if (std::string("matrix_product_state_max_bond_dimension") == key)
976 maxBondDim = value;
977 else if (std::string("matrix_product_state_truncation_threshold") == key)
979 else if (std::string("mps_sample_measure_algorithm") == key)
980 mpsSample = value;
981 else if (std::string("use_double_precision") == key)
983 (std::string("1") == value || std::string("true") == value);
984 else if (std::string("max_simulators") == key)
985 maxSimulators = std::stoull(value);
986
987 if (simulator) simulator->Configure(key, value);
988 }
989
998 std::shared_ptr<Simulators::ISimulator> GetSimulator() const override {
999 return simulator;
1000 }
1001
1011
1024 SchedulerType schType =
1026 if (!controller) return;
1027
1028 controller->CreateScheduler(BaseClass::getptr(), schType);
1029 }
1030
1039 std::shared_ptr<Schedulers::IScheduler<Time>> GetScheduler() const override {
1040 if (!controller) return nullptr;
1041
1042 return controller->GetScheduler();
1043 }
1044
1054 const std::shared_ptr<IHost<Time>> GetHost(size_t hostId) const override {
1055 if (hostId >= hosts.size()) return nullptr;
1056
1057 return hosts[hostId];
1058 }
1059
1068 const std::shared_ptr<IController<Time>> GetController() const override {
1069 return controller;
1070 }
1071
1079 size_t GetNumHosts() const override { return hosts.size(); }
1080
1089 size_t GetNumQubits() const override {
1090 size_t res = 0;
1091
1092 for (const auto &host : hosts) res += host->GetNumQubits();
1093
1094 return res;
1095 }
1096
1106 size_t GetNumQubitsForHost(size_t hostId) const override {
1107 if (hostId >= hosts.size()) return 0;
1108
1109 return hosts[hostId]->GetNumQubits();
1110 }
1111
1120 size_t GetNumNetworkEntangledQubits() const override {
1121 size_t res = 0;
1122
1123 for (const auto &host : hosts) res += host->GetNumNetworkEntangledQubits();
1124
1125 return res;
1126 }
1127
1140 size_t GetNumNetworkEntangledQubitsForHost(size_t hostId) const override {
1141 if (hostId >= hosts.size()) return 0;
1142
1143 return hosts[hostId]->GetNumNetworkEntangledQubits();
1144 }
1145
1154 size_t GetNumClassicalBits() const override {
1155 size_t res = 0;
1156
1157 for (const auto &host : hosts) res += host->GetNumClassicalBits();
1158
1159 return res;
1160 }
1161
1173 size_t GetNumClassicalBitsForHost(size_t hostId) const override {
1174 if (hostId >= hosts.size()) return 0;
1175
1176 return hosts[hostId]->GetNumClassicalBits();
1177 }
1178
1188 std::vector<std::shared_ptr<IHost<Time>>> &GetHosts() { return hosts; }
1189
1197 void SetController(const std::shared_ptr<IController<Time>> &cntrl) {
1198 controller = cntrl;
1199 }
1200
1214 bool SendPacket(size_t fromHostId, size_t toHostId,
1215 const std::vector<uint8_t> &packet) override {
1216 return false;
1217 }
1218
1226 NetworkType GetType() const override {
1228 }
1229
1241 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1242 const auto qubits = op->AffectedQubits();
1243
1244 if (qubits.empty()) return true;
1245
1246 size_t firstQubit = qubits[0];
1247
1248 for (size_t q = 1; q < qubits.size(); ++q)
1249 if (!AreQubitsOnSameHost(firstQubit, qubits[q])) return false;
1250
1251 return true;
1252 }
1253
1267 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1268 const auto qubits = op->AffectedQubits();
1269
1270 if (qubits.empty()) return false;
1271
1272 // grab the first qubit that is on a host (skip over network entangled
1273 // qubits)
1274 size_t firstQubit = qubits[0];
1275 size_t q = 1;
1276 for (; IsNetworkEntangledQubit(firstQubit) && q < qubits.size(); ++q)
1277 firstQubit = qubits[q];
1278
1279 // check to see one of the other qubits is on a different host, but ignore
1280 // the network entangled qubits
1281 for (; q < qubits.size(); ++q)
1282 if (!IsNetworkEntangledQubit(qubits[q]) &&
1283 !AreQubitsOnSameHost(firstQubit, qubits[q]))
1284 return true;
1285
1286 return false;
1287 }
1288
1301 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1302 const auto qubits = op->AffectedQubits();
1303
1304 if (qubits.empty()) return false;
1305
1306 for (size_t q = 0; q < qubits.size(); ++q)
1307 if (IsNetworkEntangledQubit(qubits[q])) return true;
1308
1309 return false;
1310 }
1311
1322 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1323 if (op->GetType() != Circuits::OperationType::kGate) return false;
1324 const auto qubits = op->AffectedQubits();
1325 if (qubits.size() != 2) return false;
1326
1327 return IsNetworkEntangledQubit(qubits[0]) &&
1328 IsNetworkEntangledQubit(qubits[1]);
1329 }
1330
1342 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1343 if (!op->IsConditional()) return false;
1344
1345 const auto qubits = op->AffectedQubits();
1346
1347 const std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1348 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1349 const auto &classicalBits = condOp->GetCondition()->GetBitsIndices();
1350
1351 if (qubits.empty() && classicalBits.empty())
1352 throw std::runtime_error(
1353 "No classical bits specified!"); // this would be odd!
1354
1355 // consider it on the host where it has the first qubit (or bit, if there
1356 // are no qubits)
1357
1358 const size_t hostId = GetHostIdForAnyQubit(qubits[0]);
1359
1360 // now check the classical bits
1361 for (const auto bit : classicalBits)
1362 if (hostId != GetHostIdForClassicalBit(bit)) return true;
1363
1364 return false;
1365 }
1366
1378 const std::shared_ptr<Circuits::IOperation<Time>> &op) const override {
1379 if (!op->IsConditional())
1380 throw std::runtime_error("Operation is not conditional!");
1381
1382 std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1383 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1384 const auto classicalBits = condOp->AffectedBits();
1385
1386 if (classicalBits.empty())
1387 throw std::runtime_error("No classical bits specified!");
1388
1389 return GetHostIdForClassicalBit(classicalBits[0]);
1390 }
1391
1400 bool AreQubitsOnSameHost(size_t qubitId1, size_t qubitId2) const override {
1401 for (const auto &host : hosts) {
1402 const bool present1 = host->IsQubitOnHost(qubitId1);
1403 const bool present2 = host->IsQubitOnHost(qubitId2);
1404
1405 if (present1 && present2)
1406 return true;
1407 else if (present1 || present2)
1408 return false;
1409 }
1410
1411 return false;
1412 }
1413
1423 bool AreClassicalBitsOnSameHost(size_t bitId1, size_t bitId2) const override {
1424 for (const auto &host : hosts) {
1425 const bool present1 = host->IsClassicalBitOnHost(bitId1);
1426 const bool present2 = host->IsClassicalBitOnHost(bitId2);
1427
1428 if (present1 && present2)
1429 return true;
1430 else if (present1 || present2)
1431 return false;
1432 }
1433
1434 return false;
1435 }
1436
1448 size_t bitId) const override {
1449 for (const auto &host : hosts) {
1450 const bool present1 = host->IsQubitOnHost(qubitId);
1451 const bool present2 = host->IsClassicalBitOnHost(bitId);
1452
1453 if (present1 && present2)
1454 return true;
1455 else if (present1 || present2)
1456 return false;
1457 }
1458
1459 return false;
1460 }
1461
1471 size_t GetHostIdForQubit(size_t qubitId) const override {
1472 for (const auto &host : hosts)
1473 if (host->IsQubitOnHost(qubitId)) return host->GetId();
1474
1475 return std::numeric_limits<size_t>::max();
1476 }
1477
1490 size_t GetHostIdForEntangledQubit(size_t qubitId) const override {
1491 for (const auto &host : hosts)
1492 if (host->IsEntangledQubitOnHost(qubitId)) return host->GetId();
1493
1494 return std::numeric_limits<size_t>::max();
1495 }
1496
1506 size_t GetHostIdForAnyQubit(size_t qubitId) const override {
1507 if (IsNetworkEntangledQubit(qubitId))
1508 return GetHostIdForEntangledQubit(qubitId);
1509
1510 return GetHostIdForQubit(qubitId);
1511 }
1512
1522 size_t GetHostIdForClassicalBit(size_t classicalBitId) const override {
1523 for (const auto &host : hosts)
1524 if (host->IsClassicalBitOnHost(classicalBitId)) return host->GetId();
1525
1526 return std::numeric_limits<size_t>::max();
1527 }
1528
1537 std::vector<size_t> GetQubitsIds(size_t hostId) const override {
1538 if (hostId >= hosts.size()) return std::vector<size_t>();
1539
1540 return hosts[hostId]->GetQubitsIds();
1541 }
1542
1553 std::vector<size_t> GetNetworkEntangledQubitsIds(
1554 size_t hostId) const override {
1555 if (hostId >= hosts.size()) return std::vector<size_t>();
1556
1557 return hosts[hostId]->GetNetworkEntangledQubitsIds();
1558 }
1559
1569 std::vector<size_t> GetClassicalBitsIds(size_t hostId) const override {
1570 if (hostId >= hosts.size()) return std::vector<size_t>();
1571
1572 return hosts[hostId]->GetClassicalBitsIds();
1573 }
1574
1586 size_t hostId) const override {
1587 if (hostId >= hosts.size()) return std::vector<size_t>();
1588
1589 return hosts[hostId]->GetEntangledQubitMeasurementBitIds();
1590 }
1591
1603 bool IsNetworkEntangledQubit(size_t qubitId) const override {
1604 return qubitId >= GetNumQubits();
1605 }
1606
1619 bool IsEntanglementQubitBusy(size_t qubitId) const override { return false; }
1620
1636 bool AreEntanglementQubitsBusy(size_t qubitId1,
1637 size_t qubitId2) const override {
1638 return false;
1639 }
1640
1653 void MarkEntangledQubitsBusy(size_t qubitId1, size_t qubitId2) override {
1654 throw std::runtime_error(
1655 "Entanglement between hosts is not supported in the simple network");
1656 }
1657
1669 void MarkEntangledQubitFree(size_t qubitId) override {
1670 throw std::runtime_error(
1671 "Entanglement between hosts is not supported in the simple network");
1672 }
1673
1683 void ClearEntanglements() override {
1684 throw std::runtime_error(
1685 "Entanglement between hosts is not supported in the simple network");
1686 }
1687
1697 std::shared_ptr<Circuits::Circuit<Time>> GetDistributedCircuit()
1698 const override {
1699 return distCirc;
1700 }
1701
1712
1721 return lastMethod;
1722 }
1723
1734 size_t GetMaxSimulators() const override { return maxSimulators; }
1735
1747 void SetMaxSimulators(size_t val) override {
1748 if (val < 1)
1749 val = 1;
1750 else if (val > (size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads())
1751 val = (size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads();
1752
1753 maxSimulators = val;
1754 }
1755
1765 void SetOptimizeSimulator(bool optimize = true) override {
1766 optimizeSimulator = optimize;
1767 }
1768
1776 bool GetOptimizeSimulator() const override { return optimizeSimulator; }
1777
1786 const typename BaseClass::SimulatorsSet &GetSimulatorsSet() const override {
1788 }
1789
1800 Simulators::SimulationType kind) override {
1801 simulatorsForOptimizations.insert({type, kind});
1802 }
1803
1816
1833
1846 Simulators::SimulationType kind) const override {
1847 if (simulatorsForOptimizations.empty()) return true;
1848
1849 return simulatorsForOptimizations.find({type, kind}) !=
1851 }
1852
1859 std::shared_ptr<INetwork<Time>> Clone() const override {
1860 const size_t numHosts = GetNumHosts();
1861
1862 std::vector<Types::qubit_t> qubits(numHosts);
1863 std::vector<size_t> cbits(numHosts);
1864
1865 for (size_t h = 0; h < numHosts; ++h) {
1866 qubits[h] = GetNumQubitsForHost(h);
1867 cbits[h] = GetNumClassicalBitsForHost(h);
1868 }
1869
1870 const auto cloned =
1871 std::make_shared<SimpleDisconnectedNetwork<Time, Controller>>(qubits,
1872 cbits);
1873
1874 cloned->maxBondDim = maxBondDim;
1875 cloned->singularValueThreshold = singularValueThreshold;
1876 cloned->mpsSample = mpsSample;
1877
1878 // cloned->optimizeSimulator = optimizeSimulator;
1879 // cloned->simulatorsForOptimizations = simulatorsForOptimizations;
1880
1881 if (GetSimulator())
1882 cloned->CreateSimulator(GetSimulator()->GetType(),
1883 GetSimulator()->GetSimulationType());
1884
1885 return cloned;
1886 }
1887
1888 std::shared_ptr<Simulators::ISimulator> ChooseBestSimulator(
1889 std::shared_ptr<Circuits::Circuit<Time>> &dcirc, size_t &counts,
1890 size_t nrQubits, size_t nrCbits, size_t nrResultCbits,
1892 std::vector<bool> &executed, bool multithreading = false,
1893 bool dontRunCircuitStart = false) const override {
1894 if (!optimizeSimulator) return nullptr;
1895
1896 if ((!simulatorsEstimator || !simulatorsEstimator->IsInitialized()) &&
1897 simulatorsForOptimizations.size() != 1)
1898 return nullptr;
1899
1900 // when multithreading is set to true it means it needs a multithreaded
1901 // simulator
1902
1903 std::vector<
1904 std::pair<Simulators::SimulatorType, Simulators::SimulationType>>
1905 simulatorTypes;
1906
1907 const bool checkTensorNetwork =
1909
1910 // the others are to be picked between statevector, composite, tensor
1911 // networks and mps, for now at least for tensor networks in the future it's
1912 // worth checking different contractors!!!!
1913 //
1914 // clifford was decided at higher level
1916 // compare qcsim with qiskit aer if qiskit aer is available, let the best
1917 // one win
1920 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1922
1923#ifndef NO_QISKIT_AER
1924 // if the number of shots is too small, probably it's not worth it, it's
1925 // going to be better to just execute them multithreading
1928 simulatorTypes.emplace_back(Simulators::SimulatorType::kQiskitAer,
1930#endif
1931 }
1932
1935 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1937
1940 simulatorTypes.emplace_back(Simulators::SimulatorType::kCompositeQCSim,
1942
1943 if (checkTensorNetwork &&
1946 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1948
1952 (nrQubits <= 4 || !maxBondDim.empty()))
1953 simulatorTypes.emplace_back(
1956
1960 simulatorTypes.emplace_back(Simulators::SimulatorType::kQCSim,
1962
1963#ifndef NO_QISKIT_AER
1964 // tensor networks are out of the picture for now for qiskit aer, since they
1965 // are available with cuda library, and work only on linux (obviously when
1966 // compiled properly and if there is the right hw an driver installed)
1967
1970 simulatorTypes.emplace_back(Simulators::SimulatorType::kQiskitAer,
1972
1976 simulatorTypes.emplace_back(
1979
1983 (nrQubits <= 4 || !maxBondDim.empty()))
1984 simulatorTypes.emplace_back(
1987#endif
1988
1989#ifdef __linux__
1993 simulatorTypes.emplace_back(Simulators::SimulatorType::kGpuSim,
1998 simulatorTypes.emplace_back(
2004 simulatorTypes.emplace_back(Simulators::SimulatorType::kGpuSim,
2009 simulatorTypes.emplace_back(
2012 }
2013#endif
2014
2017 simulatorTypes.emplace_back(Simulators::SimulatorType::kQuestSim,
2019
2020 if (simulatorTypes.empty())
2021 return nullptr;
2022 else if (simulatorTypes.size() == 1) {
2023 simType = simulatorTypes[0].first;
2024 method = simulatorTypes[0].second;
2025
2026 std::shared_ptr<Simulators::ISimulator> sim =
2028 if (sim) {
2030 if (!maxBondDim.empty())
2031 sim->Configure("matrix_product_state_max_bond_dimension",
2032 maxBondDim.c_str());
2033 if (!singularValueThreshold.empty())
2034 sim->Configure("matrix_product_state_truncation_threshold",
2035 singularValueThreshold.c_str());
2036 if (!mpsSample.empty())
2037 sim->Configure("mps_sample_measure_algorithm", mpsSample.c_str());
2038 }
2039 sim->SetMultithreading(true);
2040 if (!dontRunCircuitStart)
2042 Time>::ExecuteUpToMeasurements(dcirc, nrQubits, nrCbits,
2043 nrResultCbits, sim, executed,
2044 multithreading);
2045
2046 return sim;
2047 }
2048 }
2049
2050 return simulatorsEstimator->ChooseBestSimulator(
2051 simulatorTypes, dcirc, counts, nrQubits, nrCbits, nrResultCbits,
2052 simType, method, executed, maxBondDim, singularValueThreshold,
2053 mpsSample, GetMaxSimulators(), pauliStrings, multithreading,
2054 dontRunCircuitStart);
2055 }
2056
2057
2058 void SetInitialQubitsMapOptimization(bool optimize = true) override {
2059 optimizeInitialQubitsMap = optimize;
2060 }
2061
2062 bool GetInitialQubitsMapOptimization() const override {
2063 return optimizeInitialQubitsMap;
2064 }
2065
2066 void SetMPSOptimizationBondDimensionThreshold(size_t threshold) override {
2067 mpsOptimizationBondDimensionThreshold = threshold;
2068 }
2069
2071 return mpsOptimizationBondDimensionThreshold;
2072 }
2073
2074 void SetMPSOptimizationQubitsNumberThreshold(size_t threshold) override {
2075 mpsOptimizationQubitsNumberThreshold = threshold;
2076 }
2077
2079 return mpsOptimizationQubitsNumberThreshold;
2080 }
2081
2082 protected:
2092 auto optimiser = controller->GetOptimiser();
2093 if (optimiser) {
2094 // convert the classical state results back to the expected order
2095 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2096
2097 ConvertBackState(qubitsMap);
2098 }
2099 }
2100
2113 const std::unordered_map<Types::qubit_t, Types::qubit_t> &qubitsMap) {
2114 // might not be the one stored in the network, might exist in the DES
2115 Circuits::OperationState &theClassicalState = GetState();
2116
2117 theClassicalState.Remap(qubitsMap);
2118 }
2119
2131 auto optimiser = controller->GetOptimiser();
2132 if (optimiser) {
2133 // convert the classical state results back to the expected order
2134 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2135
2136 ConvertBackResults(res, qubitsMap);
2137 }
2138 }
2139
2153 ExecuteResults &res,
2154 const std::unordered_map<Types::qubit_t, Types::qubit_t> &bitsMap) const {
2155 ExecuteResults translatedRes;
2156
2157 size_t numClassicalBits = 0;
2158 for (const auto &[q, b] : bitsMap)
2159 if (b >= numClassicalBits) numClassicalBits = b + 1;
2160
2161 numClassicalBits = std::max(numClassicalBits, GetNumClassicalBits());
2162
2163 for (const auto &r : res) {
2164 Circuits::OperationState translatedState(r.first);
2165
2166 translatedState.Remap(bitsMap, false, numClassicalBits);
2167 translatedRes[translatedState.GetAllBits()] = r.second;
2168 }
2169
2170 res.swap(translatedRes);
2171 }
2172
2184 std::unordered_map<Types::qubit_t, Types::qubit_t> MapCircuitOnHost(
2185 const std::shared_ptr<Circuits::Circuit<Time>> &circuit, size_t hostId,
2186 size_t &nrQubits, size_t &nrCbits, bool useSeparateSimForHosts = false) {
2187 qubitsMapOnHost.clear();
2188 nrQubits = 0;
2189 nrCbits = 0;
2190 if (!circuit) return {};
2191
2192 const auto host =
2193 std::static_pointer_cast<SimpleHost<Time>>(GetHost(hostId));
2194 const size_t hostNrQubits = host->GetNumQubits();
2195
2196 std::unordered_map<Types::qubit_t, Types::qubit_t> reverseQubitsMap;
2197
2198 if (!useSeparateSimForHosts) {
2199 size_t mxq = 0;
2200 size_t mnq = std::numeric_limits<size_t>::max();
2201 size_t mxb = 0;
2202 size_t mnb = std::numeric_limits<size_t>::max();
2203
2204 for (const auto &op : circuit->GetOperations()) {
2205 const auto qbits = op->AffectedQubits();
2206 for (auto q : qbits) {
2207 if (q > mxq) mxq = q;
2208 if (q < mnq) mnq = q;
2209 }
2210 const auto cbits = op->AffectedBits();
2211 for (auto b : cbits) {
2212 if (b > mxb) mxb = b;
2213 if (b < mnb) mnb = b;
2214 }
2215 }
2216
2217 if (mnq > mxq) mnq = 0;
2218 if (mnb > mxb) mnb = 0;
2219
2220 nrQubits = mxq - mnq + 1;
2221 nrCbits = mxb - mnb + 1;
2222 if (nrCbits < nrQubits) nrCbits = nrQubits;
2223
2224 const size_t startQubit = host->GetStartQubitId();
2225
2226 if (mnq < startQubit || mxq >= startQubit + hostNrQubits) {
2227 if (nrQubits >
2228 hostNrQubits +
2229 1) // the host has an additional 'special' qubit for the
2230 // entanglement or other operations (like those for cutting)
2231 throw std::runtime_error("Circuit does not fit on the host!");
2232
2233 for (size_t i = 0; i < nrCbits; ++i) {
2234 const size_t mapFrom = mnq + i;
2235 const size_t mapTo = startQubit + i;
2236
2237 qubitsMapOnHost[mapFrom] = mapTo;
2238 reverseQubitsMap[mapTo] = mapFrom;
2239 }
2240
2241 distCirc = std::static_pointer_cast<Circuits::Circuit<Time>>(
2242 circuit->Remap(qubitsMapOnHost, qubitsMapOnHost));
2243 }
2244
2245 return reverseQubitsMap;
2246 }
2247
2248 distCirc = circuit->RemapToContinuous(qubitsMapOnHost, reverseQubitsMap,
2249 nrQubits, nrCbits);
2250
2251 assert(nrQubits == qubitsMapOnHost.size());
2252
2253 if (nrQubits == 0) nrQubits = 1;
2254
2255 if (nrQubits >
2256 hostNrQubits +
2257 1) // the host has an additional 'special' qubit for the
2258 // entanglement or other operations (like those for cutting)
2259 throw std::runtime_error("Circuit does not fit on the host!");
2260
2261 return reverseQubitsMap;
2262 }
2263
2264 bool optimizeSimulator = true;
2266
2273 std::string maxBondDim;
2275 std::string mpsSample;
2277
2278 size_t maxSimulators = QC::QubitRegisterCalculator<>::
2279 GetNumberOfThreads();
2284 std::shared_ptr<Simulators::ISimulator>
2287 std::shared_ptr<Circuits::Circuit<Time>>
2290 std::shared_ptr<IController<Time>>
2292 // TODO: depending on the network topology, we will have adiacency lists, etc.
2293 // or simply a vector of hosts for a totally connected network (or where the
2294 // communication details do not matter so much)
2295 std::vector<std::shared_ptr<IHost<Time>>>
2298 std::unique_ptr<Estimators::SimulatorsEstimatorInterface<Time>>
2301 private:
2303 threadsPool;
2304 bool recreateIfNeeded =
2305 true;
2306 std::unordered_map<Types::qubit_t, Types::qubit_t>
2307 qubitsMapOnHost;
2310 const std::vector<std::string> *pauliStrings =
2311 nullptr;
2314 bool optimizeInitialQubitsMap = true;
2316 size_t mpsOptimizationBondDimensionThreshold =
2317 32;
2318 size_t mpsOptimizationQubitsNumberThreshold =
2319 12;
2320};
2321
2322} // namespace Network
2323
2324#endif // !_SIMPLE_NETWORK_H_
Circuit class for holding the sequence of operations.
Definition Circuit.h:46
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 AllocateBits(size_t numBits)
Allocate more bits.
Definition Operations.h:160
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
The controller host interface.
Definition Controller.h:106
The network interface.
Definition Network.h:58
std::unordered_set< SimulatorPair, boost::hash< SimulatorPair > > SimulatorsSet
Definition Network.h:64
std::shared_ptr< INetwork< Time > > getptr()
Get a shared pointer to this object.
Definition Network.h:772
typename Circuits::Circuit< Time >::ExecuteResults ExecuteResults
Definition Network.h:60
The simple network implementation.
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.
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.
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 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 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.
typename BaseClass::ExecuteResults ExecuteResults
The execute results type.
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
Choose the best simulator for the given circuit.
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.
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.
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.
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
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
NetworkType
The type of the network.
Definition Network.h:34
@ kSimpleDisconnectedNetwork
Simple network, no communication among hosts, sequential simulation.
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
@ kMatrixProductState
matrix product state simulation type
@ kStabilizer
Clifford gates simulation type.
@ kPauliPropagator
Pauli propagator simulation type.
@ kTensorNetwork
Tensor network simulation type.
SimulatorType
The type of simulator.
Definition State.h:68
@ kCompositeQCSim
composite qcsim simulator type
@ kQCSim
qcsim simulator type
@ kQiskitAer
qiskit aer simulator type
@ kQuestSim
quest simulator type
@ kCompositeQiskitAer
composite qiskit aer simulator type
@ kGpuSim
gpu simulator type
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