15#ifndef _SIMPLE_NETWORK_H_
16#define _SIMPLE_NETWORK_H_
18#include "QubitRegister.h"
42 class Controller = SimpleController<Time>>
59 const std::vector<size_t> &cbits = {}) {
73 const std::vector<size_t> &cbits) {
74 size_t qubitsOffset = 0;
75 size_t cbitsOffset = 0;
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);
81 i, qubitsOffset, numQubits, cbitsOffset, numBits));
82 qubitsOffset += numQubits;
83 cbitsOffset += numBits;
86 for (
size_t i = 0; i <
hosts.size(); ++i) {
87 std::static_pointer_cast<SimpleHost<Time>>(
hosts[i])->SetEntangledQubitId(
89 std::static_pointer_cast<SimpleHost<Time>>(
hosts[i])
90 ->SetEntangledQubitMeasurementBit(cbitsOffset);
110 const auto recreate = recreateIfNeeded;
114 size_t numQubits = 2;
118 numQubits =
simulator->GetNumberOfQubits();
121 recreateIfNeeded =
false;
125 recreateIfNeeded = recreate;
129 const auto &first = *res.begin();
136 method !=
simulator->GetSimulationType() ||
137 simulator->GetNumberOfQubits() != numQubits))))
154 size_t hostId)
override {
155 const auto recreate = recreateIfNeeded;
159 size_t numQubits = 2;
163 numQubits =
simulator->GetNumberOfQubits();
166 recreateIfNeeded =
false;
170 recreateIfNeeded = recreate;
174 const auto &first = *res.begin();
181 method !=
simulator->GetSimulationType() ||
182 simulator->GetNumberOfQubits() != numQubits))))
203 const std::vector<std::string> &paulis)
override {
204 const auto recreate = recreateIfNeeded;
208 size_t numQubits = 2;
212 numQubits =
simulator->GetNumberOfQubits();
215 recreateIfNeeded =
false;
217 pauliStrings = &paulis;
219 pauliStrings =
nullptr;
221 recreateIfNeeded = recreate;
225 const auto &first = *res.begin();
229 std::vector<double> expectations(paulis.size());
232 const size_t numOps =
simulator->GetNumberOfQubits();
237 const auto &qubitsMap = optimiser->GetQubitsMap();
239 for (
size_t i = 0; i < paulis.size(); ++i) {
240 std::string translated(numOps,
'I');
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];
247 translated[j] = paulis[i][j];
250 expectations[i] =
simulator->ExpectationValue(translated);
253 for (
size_t i = 0; i < paulis.size(); ++i)
254 expectations[i] =
simulator->ExpectationValue(paulis[i]);
259 method !=
simulator->GetSimulationType() ||
260 simulator->GetNumberOfQubits() != numQubits))
283 const std::vector<std::string> &paulis)
override {
284 const auto recreate = recreateIfNeeded;
288 size_t numQubits = 2;
292 numQubits =
simulator->GetNumberOfQubits();
296 struct ScopedRestore {
298 const std::vector<std::string> **ps;
299 ScopedRestore(
bool &f,
const std::vector<std::string> **p)
300 : flag(f), saved(f), ps(p) {
307 } restoreGuard(recreateIfNeeded, &pauliStrings);
309 pauliStrings = &paulis;
314 const auto &first = *res.begin();
322 const size_t offsetBase = qubitsMapOnHost.size();
324 std::vector<double> expectations(paulis.size(), 1.);
327 const size_t numOps =
simulator->GetNumberOfQubits();
330 for (
size_t i = 0; i < paulis.size(); ++i) {
331 std::string translated(std::max(numOps, paulis[i].size()),
'I');
333 size_t offset = offsetBase;
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];
340 translated[offset] = paulis[i][j];
347 expectations[i] =
simulator->ExpectationValue(translated);
350 throw std::runtime_error(
351 "ExecuteOnHostExpectations: no simulator available after execution.");
355 method !=
simulator->GetSimulationType() ||
356 simulator->GetNumberOfQubits() != numQubits))
375 size_t hostId)
override {
376 const auto recreate = recreateIfNeeded;
380 size_t numQubits = 2;
384 numQubits =
simulator->GetNumberOfQubits();
388 struct ScopedRestoreFlag {
390 ScopedRestoreFlag(
bool &f) : flag(f), saved(f) { flag =
false; }
391 ~ScopedRestoreFlag() { flag = saved; }
392 } restoreGuard(recreateIfNeeded);
397 const auto &first = *res.begin();
402 throw std::runtime_error(
403 "ExecuteOnHostAmplitudes: no simulator available after execution.");
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);
414 if (!qubitsMapOnHost.empty()) {
415 const size_t offsetBase = qubitsMapOnHost.size();
419 std::vector<size_t> simToOrig(n);
420 size_t offset = offsetBase;
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;
427 simToOrig[qbit] = offset++;
430 std::vector<std::complex<double>> remapped(dim);
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]);
438 if (orig_state < dim) remapped[orig_state] = amplitudes[sim_state];
440 amplitudes.swap(remapped);
444 method !=
simulator->GetSimulationType() ||
445 simulator->GetNumberOfQubits() != numQubits))
466 size_t hostId)
override {
467 const auto recreate = recreateIfNeeded;
471 size_t numQubits = 2;
475 numQubits =
simulator->GetNumberOfQubits();
479 struct ScopedRestoreFlag {
481 ScopedRestoreFlag(
bool &f) : flag(f), saved(f) { flag =
false; }
482 ~ScopedRestoreFlag() { flag = saved; }
483 } restoreGuard(recreateIfNeeded);
488 const auto &first = *res.begin();
493 throw std::runtime_error(
494 "ExecuteOnHostProjectOnZero: no simulator available after "
497 const std::complex<double> result =
simulator->ProjectOnZero();
500 method !=
simulator->GetSimulationType() ||
501 simulator->GetNumberOfQubits() != numQubits))
522 size_t shots = 1000)
override {
529 for (
auto q :
distCirc->AffectedQubits()) {
532 <<
"This is a distributed circuit, using entanglement or cutting"
542 if (
distCirc->HasOpsAfterMeasurements() &&
548 distCirc->MoveMeasurementsAndResets();
550 auto method =
simulator->GetSimulationType();
552 const auto saveSimType = simType;
553 const auto saveMethod = method;
577 simulator->GetConfiguration(
"matrix_product_state_max_bond_dimension");
579 "matrix_product_state_truncation_threshold");
590 std::vector<bool> executed;
593 simType, method, executed);
606 !
distCirc->HasOpsAfterMeasurements()) ||
610 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
612 std::mutex resultsMutex;
630 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
632 threadsPool.Resize(nrThreads);
633 threadsPool.SetFinishLimit(shots);
636 const size_t curCnt = std::min(cntPerThread, shots);
640 auto job = std::make_shared<ExecuteJob<Time>>(
641 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
642 method, resultsMutex);
652 job->optSim = optSim->Clone();
653 job->executedGates = executed;
656 threadsPool.AddRunJob(std::move(job));
659 threadsPool.WaitForFinish();
662 const size_t curCnt = shots;
664 auto job = std::make_shared<ExecuteJob<Time>>(
665 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
666 method, resultsMutex);
676 optSim->SetMultithreading(
true);
677 job->optSim = optSim;
678 job->executedGates = executed;
680 if (
simulator && method == saveMethod && simType == saveSimType) {
683 job->optSim = optSim;
685 optSim->GetNumberOfQubits());
686 job->executedGates.resize(dcirc->size(),
693 if (!recreateIfNeeded)
simulator = job->optSim;
720 size_t shots = 1000)
override {
721 if (!circuit || hostId >=
GetNumHosts())
return {};
726 std::shared_ptr<Circuits::Circuit<Time>> optCircuit;
729 std::static_pointer_cast<Circuits::Circuit<Time>>(circuit->Clone());
730 optCircuit->Optimize();
733 GetController()->GetOptimizeCircuit() ? optCircuit : circuit, hostId,
734 nrQubits, nrCbits,
true);
735 if (nrCbits == 0) nrCbits = nrQubits;
742 simulator->GetConfiguration(
"matrix_product_state_max_bond_dimension");
744 "matrix_product_state_truncation_threshold");
747 if (
distCirc->HasOpsAfterMeasurements() &&
753 distCirc->MoveMeasurementsAndResets();
755 auto method =
simulator->GetSimulationType();
756 const auto saveSimType = simType;
757 const auto saveMethod = method;
784 std::vector<bool> executed;
786 nrCbits, simType, method, executed);
800 !
distCirc->HasOpsAfterMeasurements()) ||
804 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
809 std::mutex resultsMutex;
817 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
819 threadsPool.Resize(nrThreads);
820 threadsPool.SetFinishLimit(shots);
823 const size_t curCnt = std::min(cntPerThread, shots);
826 auto job = std::make_shared<ExecuteJob<Time>>(
827 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
838 job->optSim = optSim->Clone();
839 job->executedGates = executed;
842 threadsPool.AddRunJob(std::move(job));
845 threadsPool.WaitForFinish();
848 const size_t curCnt = shots;
850 auto job = std::make_shared<ExecuteJob<Time>>(
851 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
862 optSim->SetMultithreading(
true);
863 job->optSim = optSim;
864 job->executedGates = executed;
868 if (!recreateIfNeeded)
simulator = job->optSim;
888 if (!circuit)
return 0;
890 size_t distgates = 0;
892 for (
const auto &op : circuit->GetOperations())
949 size_t nrQubits = 0)
override {
959 simulator->Configure(
"matrix_product_state_max_bond_dimension",
962 simulator->Configure(
"matrix_product_state_truncation_threshold",
973 simulator->setGrowthFactorGate(growthFactorGate);
974 simulator->setGrowthFactorSwap(growthFactorSwap);
975 simulator->SetLookaheadDepth(lookaheadDepth);
976 simulator->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
989 void Configure(
const char *key,
const char *value)
override {
990 if (!key || !value)
return;
992 if (std::string(
"matrix_product_state_max_bond_dimension") == key)
994 else if (std::string(
"matrix_product_state_truncation_threshold") == key)
996 else if (std::string(
"mps_sample_measure_algorithm") == key)
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)
1056 std::shared_ptr<Schedulers::IScheduler<Time>>
GetScheduler()
const override {
1071 const std::shared_ptr<IHost<Time>>
GetHost(
size_t hostId)
const override {
1072 if (hostId >=
hosts.size())
return nullptr;
1074 return hosts[hostId];
1109 for (
const auto &host :
hosts) res += host->GetNumQubits();
1124 if (hostId >=
hosts.size())
return 0;
1126 return hosts[hostId]->GetNumQubits();
1140 for (
const auto &host :
hosts) res += host->GetNumNetworkEntangledQubits();
1158 if (hostId >=
hosts.size())
return 0;
1160 return hosts[hostId]->GetNumNetworkEntangledQubits();
1174 for (
const auto &host :
hosts) res += host->GetNumClassicalBits();
1191 if (hostId >=
hosts.size())
return 0;
1193 return hosts[hostId]->GetNumClassicalBits();
1232 const std::vector<uint8_t> &packet)
override {
1259 const auto qubits = op->AffectedQubits();
1261 if (qubits.empty())
return true;
1263 size_t firstQubit = qubits[0];
1265 for (
size_t q = 1; q < qubits.size(); ++q)
1285 const auto qubits = op->AffectedQubits();
1287 if (qubits.empty())
return false;
1291 size_t firstQubit = qubits[0];
1294 firstQubit = qubits[q];
1298 for (; q < qubits.size(); ++q)
1319 const auto qubits = op->AffectedQubits();
1321 if (qubits.empty())
return false;
1323 for (
size_t q = 0; q < qubits.size(); ++q)
1341 const auto qubits = op->AffectedQubits();
1342 if (qubits.size() != 2)
return false;
1360 if (!op->IsConditional())
return false;
1362 const auto qubits = op->AffectedQubits();
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();
1368 if (qubits.empty() && classicalBits.empty())
1369 throw std::runtime_error(
1370 "No classical bits specified!");
1378 for (
const auto bit : classicalBits)
1396 if (!op->IsConditional())
1397 throw std::runtime_error(
"Operation is not conditional!");
1399 std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1400 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1401 const auto classicalBits = condOp->AffectedBits();
1403 if (classicalBits.empty())
1404 throw std::runtime_error(
"No classical bits specified!");
1418 for (
const auto &host :
hosts) {
1419 const bool present1 = host->IsQubitOnHost(qubitId1);
1420 const bool present2 = host->IsQubitOnHost(qubitId2);
1422 if (present1 && present2)
1424 else if (present1 || present2)
1441 for (
const auto &host :
hosts) {
1442 const bool present1 = host->IsClassicalBitOnHost(bitId1);
1443 const bool present2 = host->IsClassicalBitOnHost(bitId2);
1445 if (present1 && present2)
1447 else if (present1 || present2)
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);
1470 if (present1 && present2)
1472 else if (present1 || present2)
1489 for (
const auto &host :
hosts)
1490 if (host->IsQubitOnHost(qubitId))
return host->GetId();
1492 return std::numeric_limits<size_t>::max();
1508 for (
const auto &host :
hosts)
1509 if (host->IsEntangledQubitOnHost(qubitId))
return host->GetId();
1511 return std::numeric_limits<size_t>::max();
1540 for (
const auto &host :
hosts)
1541 if (host->IsClassicalBitOnHost(classicalBitId))
return host->GetId();
1543 return std::numeric_limits<size_t>::max();
1555 if (hostId >=
hosts.size())
return std::vector<size_t>();
1557 return hosts[hostId]->GetQubitsIds();
1571 size_t hostId)
const override {
1572 if (hostId >=
hosts.size())
return std::vector<size_t>();
1574 return hosts[hostId]->GetNetworkEntangledQubitsIds();
1587 if (hostId >=
hosts.size())
return std::vector<size_t>();
1589 return hosts[hostId]->GetClassicalBitsIds();
1603 size_t hostId)
const override {
1604 if (hostId >=
hosts.size())
return std::vector<size_t>();
1606 return hosts[hostId]->GetEntangledQubitMeasurementBitIds();
1654 size_t qubitId2)
const override {
1671 throw std::runtime_error(
1672 "Entanglement between hosts is not supported in the simple network");
1687 throw std::runtime_error(
1688 "Entanglement between hosts is not supported in the simple network");
1701 throw std::runtime_error(
1702 "Entanglement between hosts is not supported in the simple network");
1767 else if (val > (
size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads())
1768 val = (size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads();
1876 std::shared_ptr<INetwork<Time>>
Clone()
const override {
1879 std::vector<Types::qubit_t> qubits(numHosts);
1880 std::vector<size_t> cbits(numHosts);
1882 for (
size_t h = 0; h < numHosts; ++h) {
1888 std::make_shared<SimpleDisconnectedNetwork<Time, Controller>>(qubits,
1918 size_t nrQubits,
size_t nrCbits,
size_t nrResultCbits,
1920 std::vector<bool> &executed,
bool multithreading =
false,
1921 bool dontRunCircuitStart =
false)
const override {
1932 std::pair<Simulators::SimulatorType, Simulators::SimulationType>>
1935 const bool checkTensorNetwork =
1951#ifndef NO_QISKIT_AER
1971 if (checkTensorNetwork &&
1981 simulatorTypes.emplace_back(
1997#ifndef NO_QISKIT_AER
2010 simulatorTypes.emplace_back(
2018 simulatorTypes.emplace_back(
2032 simulatorTypes.emplace_back(
2043 simulatorTypes.emplace_back(
2054 if (simulatorTypes.empty())
2056 else if (simulatorTypes.size() == 1) {
2057 simType = simulatorTypes[0].first;
2058 method = simulatorTypes[0].second;
2060 std::shared_ptr<Simulators::ISimulator> sim =
2065 sim->Configure(
"matrix_product_state_max_bond_dimension",
2068 sim->Configure(
"matrix_product_state_truncation_threshold",
2071 sim->Configure(
"mps_sample_measure_algorithm",
mpsSample.c_str());
2073 sim->AllocateQubits(nrQubits);
2076 sim->setGrowthFactorGate(growthFactorGate);
2077 sim->setGrowthFactorSwap(growthFactorSwap);
2078 sim->SetLookaheadDepth(lookaheadDepth);
2079 sim->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
2083 sim->AllocateQubits(nrQubits);
2087 if (!dontRunCircuitStart) {
2088 sim->SetMultithreading(
true);
2090 Time>::ExecuteUpToMeasurements(dcirc, nrQubits, nrCbits,
2091 nrResultCbits, sim, executed);
2099 std::shared_ptr<Simulators::ISimulator> sim =
2101 simulatorTypes, dcirc, counts, nrQubits, nrCbits, nrResultCbits,
2106 sim->AllocateQubits(nrQubits);
2109 sim->setGrowthFactorGate(growthFactorGate);
2110 sim->setGrowthFactorSwap(growthFactorSwap);
2111 sim->SetLookaheadDepth(lookaheadDepth);
2112 sim->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
2116 if (!dontRunCircuitStart) {
2117 sim->SetMultithreading(
true);
2119 dcirc, nrQubits, nrCbits, nrResultCbits, sim, executed);
2128 optimizeInitialQubitsMap = optimize;
2132 return optimizeInitialQubitsMap;
2136 mpsOptimizeSwaps = optimize;
2140 simulator->SetLookaheadDepthWithHeuristic(0);
2147 mpsOptimizationBondDimensionThreshold = threshold;
2150 std::stoull(
simulator->GetConfiguration(
2151 "matrix_product_state_max_bond_dimension")) < threshold) {
2153 simulator->SetLookaheadDepthWithHeuristic(0);
2158 return mpsOptimizationBondDimensionThreshold;
2162 mpsOptimizationQubitsNumberThreshold = threshold;
2166 simulator->SetLookaheadDepthWithHeuristic(0);
2171 return mpsOptimizationQubitsNumberThreshold;
2175 if (depth < 0) depth = std::numeric_limits<int>::max();
2177 lookaheadDepth = depth;
2179 if (
simulator && lookaheadDepth != std::numeric_limits<int>::max()) {
2181 simulator->SetLookaheadDepthWithHeuristic(0);
2188 if (depth < 0) depth = std::numeric_limits<int>::max();
2190 if (depth > lookaheadDepth) depth = lookaheadDepth;
2192 lookaheadDepthWithHeuristic = depth;
2194 if (
simulator && lookaheadDepthWithHeuristic != std::numeric_limits<int>::max())
2195 simulator->SetLookaheadDepthWithHeuristic(depth);
2199 return lookaheadDepthWithHeuristic;
2206 growthFactorSwap = factor;
2212 growthFactorGate = factor;
2219 std::shared_ptr<Simulators::ISimulator> &sim,
2221 if (sim->GetSimulationType() ==
2223 (optimizeInitialQubitsMap || mpsOptimizeSwaps) &&
2224 sim->SupportsMPSSwapOptimization()) {
2225 if (mpsOptimizationQubitsNumberThreshold <= nrQubits) {
2226 const auto maxBondDimValue =
2230 static_cast<int>(mpsOptimizationBondDimensionThreshold) <= maxBondDimValue) {
2232 dcirc->ConvertForCutting();
2233 auto layers = dcirc->ToMultipleQubitsLayersNoClone();
2242 if (optimizeInitialQubitsMap) {
2244 sim->SetInitialQubitsMap(optimalMap);
2248 dcirc->SetOperations(optCirc->GetOperations());
2250 if (mpsOptimizeSwaps) {
2252 int lookaheadDepthLocal = lookaheadDepth;
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) {
2263 avgTwoQubitGatesPerLayer += twoQubitGates;
2265 avgTwoQubitGatesPerLayer /= layers.size();
2267 int lookaheadVal =
static_cast<int>(4. * avgTwoQubitGatesPerLayer);
2268 if (lookaheadVal > 15) lookaheadVal = 15;
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)
2277 int lookaheadHeuristicDepthLocal = lookaheadDepthWithHeuristic;
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;
2285 if (lookaheadHeuristicDepthLocal < 0)
2286 lookaheadHeuristicDepthLocal = 0;
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());
2312 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2330 const std::unordered_map<Types::qubit_t, Types::qubit_t> &qubitsMap) {
2334 theClassicalState.
Remap(qubitsMap);
2351 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2371 const std::unordered_map<Types::qubit_t, Types::qubit_t> &bitsMap)
const {
2374 size_t numClassicalBits = 0;
2375 for (
const auto &[q, b] : bitsMap)
2376 if (b >= numClassicalBits) numClassicalBits = b + 1;
2380 for (
const auto &r : res) {
2383 translatedState.
Remap(bitsMap,
false, numClassicalBits);
2384 translatedRes[translatedState.
GetAllBits()] = r.second;
2387 res.swap(translatedRes);
2403 size_t &nrQubits,
size_t &nrCbits,
bool useSeparateSimForHosts =
false) {
2404 qubitsMapOnHost.clear();
2407 if (!circuit)
return {};
2410 std::static_pointer_cast<SimpleHost<Time>>(
GetHost(hostId));
2411 const size_t hostNrQubits = host->GetNumQubits();
2413 std::unordered_map<Types::qubit_t, Types::qubit_t> reverseQubitsMap;
2415 if (!useSeparateSimForHosts) {
2417 size_t mnq = std::numeric_limits<size_t>::max();
2419 size_t mnb = std::numeric_limits<size_t>::max();
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;
2427 const auto cbits = op->AffectedBits();
2428 for (
auto b : cbits) {
2429 if (b > mxb) mxb = b;
2430 if (b < mnb) mnb = b;
2434 if (mnq > mxq) mnq = 0;
2435 if (mnb > mxb) mnb = 0;
2437 nrQubits = mxq - mnq + 1;
2438 nrCbits = mxb - mnb + 1;
2439 if (nrCbits < nrQubits) nrCbits = nrQubits;
2441 const size_t startQubit = host->GetStartQubitId();
2443 if (mnq < startQubit || mxq >= startQubit + hostNrQubits) {
2448 throw std::runtime_error(
"Circuit does not fit on the host!");
2450 for (
size_t i = 0; i < nrCbits; ++i) {
2451 const size_t mapFrom = mnq + i;
2452 const size_t mapTo = startQubit + i;
2454 qubitsMapOnHost[mapFrom] = mapTo;
2455 reverseQubitsMap[mapTo] = mapFrom;
2458 distCirc = std::static_pointer_cast<Circuits::Circuit<Time>>(
2459 circuit->Remap(qubitsMapOnHost, qubitsMapOnHost));
2462 return reverseQubitsMap;
2465 distCirc = circuit->RemapToContinuous(qubitsMapOnHost, reverseQubitsMap,
2468 assert(nrQubits == qubitsMapOnHost.size());
2470 if (nrQubits == 0) nrQubits = 1;
2476 throw std::runtime_error(
"Circuit does not fit on the host!");
2478 return reverseQubitsMap;
2496 GetNumberOfThreads();
2501 std::shared_ptr<Simulators::ISimulator>
2504 std::shared_ptr<Circuits::Circuit<Time>>
2507 std::shared_ptr<IController<Time>>
2512 std::vector<std::shared_ptr<IHost<Time>>>
2515 std::unique_ptr<Estimators::SimulatorsEstimatorInterface<Time>>
2521 bool recreateIfNeeded =
2523 std::unordered_map<Types::qubit_t, Types::qubit_t>
2527 const std::vector<std::string> *pauliStrings =
2531 bool optimizeInitialQubitsMap =
true;
2533 bool mpsOptimizeSwaps =
true;
2534 size_t mpsOptimizationBondDimensionThreshold =
2536 size_t mpsOptimizationQubitsNumberThreshold =
2539 int lookaheadDepth =
2540 std::numeric_limits<int>::max();
2542 int lookaheadDepthWithHeuristic = std::numeric_limits<int>::max();
2545 double growthFactorSwap = 1.;
2546 double growthFactorGate = 0.7;
int GetSimulationType(void *sim)
Circuit class for holding the sequence of operations.
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.
The state class that stores the classical state of a quantum circuit execution.
const std::vector< bool > & GetAllBits() const
Get the classical bits.
void Clear()
Clear the classical state.
void SetResultsInOrder(const std::vector< bool > &results)
Set the classical bits.
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.
An interface for runtime estimators.
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.
std::shared_ptr< INetwork< Time > > getptr()
typename Circuits::Circuit< Time >::ExecuteResults ExecuteResults
std::unordered_set< SimulatorPair, boost::hash< SimulatorPair > > SimulatorsSet
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.
std::string singularValueThreshold
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.
int GetLookaheadDepthWithHeuristic() const override
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.
BaseClass::SimulatorsSet simulatorsForOptimizations
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.
double getGrowthFactorSwap() const override
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.
void SetLookaheadDepth(int depth) override
bool GetInitialQubitsMapOptimization() const override
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.
bool GetMPSOptimizeSwaps() const override
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
int GetLookaheadDepth() const 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.
double getGrowthFactorGate() const override
size_t GetMPSOptimizationQubitsNumberThreshold() const override
The simple host implementation.
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 bool IsGpuLibraryAvailable()
static std::shared_ptr< ISimulator > CreateSimulator(SimulatorType t=SimulatorType::kQCSim, SimulationType method=SimulationType::kMatrixProductState)
Create a quantum computing simulator.
ThreadsPool class for holding and controlling a pool of threads.
@ kGate
the usual quantum gate, result stays in simulator's state
NetworkType
The type of the network.
@ kSimpleDisconnectedNetwork
Simple network, no communication among hosts, sequential simulation.
SchedulerType
The type of the network scheduler for scheduling execution of multiple circuits.
@ kNoEntanglementQubitsParallel
SimulationType
The type of simulation.
@ 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.
@ kPathIntegral
Path integral simulation type.
SimulatorType
The type of simulator.
@ 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.
A way to pack together a circuit and the number of shots for its execution.