15#ifndef _SIMPLE_NETWORK_H_
16#define _SIMPLE_NETWORK_H_
18#include "QubitRegister.h"
44 class Controller = SimpleController<Time>>
61 const std::vector<size_t> &cbits = {}) {
124 const std::vector<size_t> &cbits) {
125 size_t qubitsOffset = 0;
126 size_t cbitsOffset = 0;
128 for (
size_t i = 0; i < qubits.size(); ++i) {
129 const size_t numQubits = qubits[i];
130 const size_t numBits = (i < cbits.size() ? cbits[i] : 0);
132 i, qubitsOffset, numQubits, cbitsOffset, numBits));
133 qubitsOffset += numQubits;
134 cbitsOffset += numBits;
137 for (
size_t i = 0; i <
hosts.size(); ++i) {
138 std::static_pointer_cast<SimpleHost<Time>>(
hosts[i])->SetEntangledQubitId(
140 std::static_pointer_cast<SimpleHost<Time>>(
hosts[i])
141 ->SetEntangledQubitMeasurementBit(cbitsOffset);
161 const auto recreate = recreateIfNeeded;
165 size_t numQubits = 2;
169 numQubits =
simulator->GetNumberOfQubits();
172 recreateIfNeeded =
false;
176 recreateIfNeeded = recreate;
180 const auto &first = *res.begin();
187 method !=
simulator->GetSimulationType() ||
188 simulator->GetNumberOfQubits() != numQubits))))
205 size_t hostId)
override {
206 const auto recreate = recreateIfNeeded;
210 size_t numQubits = 2;
214 numQubits =
simulator->GetNumberOfQubits();
217 recreateIfNeeded =
false;
221 recreateIfNeeded = recreate;
225 const auto &first = *res.begin();
232 method !=
simulator->GetSimulationType() ||
233 simulator->GetNumberOfQubits() != numQubits))))
254 const std::vector<std::string> &paulis)
override {
255 const auto recreate = recreateIfNeeded;
259 size_t numQubits = 2;
263 numQubits =
simulator->GetNumberOfQubits();
266 recreateIfNeeded =
false;
268 pauliStrings = &paulis;
270 pauliStrings =
nullptr;
272 recreateIfNeeded = recreate;
276 const auto &first = *res.begin();
280 std::vector<double> expectations(paulis.size());
283 const size_t numOps =
simulator->GetNumberOfQubits();
288 const auto &qubitsMap = optimiser->GetQubitsMap();
290 for (
size_t i = 0; i < paulis.size(); ++i) {
291 std::string translated(numOps,
'I');
293 for (
size_t j = 0; j < paulis[i].size(); ++j) {
294 const auto pos = qubitsMap.find(j);
295 if (pos != qubitsMap.end())
296 translated[pos->second] = paulis[i][j];
298 translated[j] = paulis[i][j];
301 expectations[i] =
simulator->ExpectationValue(translated);
304 for (
size_t i = 0; i < paulis.size(); ++i)
305 expectations[i] =
simulator->ExpectationValue(paulis[i]);
310 method !=
simulator->GetSimulationType() ||
311 simulator->GetNumberOfQubits() != numQubits))
334 const std::vector<std::string> &paulis)
override {
335 const auto recreate = recreateIfNeeded;
339 size_t numQubits = 2;
343 numQubits =
simulator->GetNumberOfQubits();
347 struct ScopedRestore {
349 const std::vector<std::string> **ps;
350 ScopedRestore(
bool &f,
const std::vector<std::string> **p)
351 : flag(f), saved(f), ps(p) {
358 } restoreGuard(recreateIfNeeded, &pauliStrings);
360 pauliStrings = &paulis;
365 const auto &first = *res.begin();
373 const size_t offsetBase = qubitsMapOnHost.size();
375 std::vector<double> expectations(paulis.size(), 1.);
378 const size_t numOps =
simulator->GetNumberOfQubits();
381 for (
size_t i = 0; i < paulis.size(); ++i) {
382 std::string translated(std::max(numOps, paulis[i].size()),
'I');
384 size_t offset = offsetBase;
386 for (
size_t j = 0; j < paulis[i].size(); ++j) {
387 auto pos = qubitsMapOnHost.find(j);
388 if (pos != qubitsMapOnHost.end())
389 translated[pos->second] = paulis[i][j];
391 translated[offset] = paulis[i][j];
398 expectations[i] =
simulator->ExpectationValue(translated);
401 throw std::runtime_error(
402 "ExecuteOnHostExpectations: no simulator available after execution.");
406 method !=
simulator->GetSimulationType() ||
407 simulator->GetNumberOfQubits() != numQubits))
426 size_t hostId)
override {
427 const auto recreate = recreateIfNeeded;
431 size_t numQubits = 2;
435 numQubits =
simulator->GetNumberOfQubits();
439 struct ScopedRestoreFlag {
441 ScopedRestoreFlag(
bool &f) : flag(f), saved(f) { flag =
false; }
442 ~ScopedRestoreFlag() { flag = saved; }
443 } restoreGuard(recreateIfNeeded);
448 const auto &first = *res.begin();
453 throw std::runtime_error(
454 "ExecuteOnHostAmplitudes: no simulator available after execution.");
456 std::vector<std::complex<double>> amplitudes;
457 const size_t n =
simulator->GetNumberOfQubits();
458 const size_t dim = 1ULL << n;
459 amplitudes.resize(dim);
460 for (
size_t state = 0; state < dim; ++state)
461 amplitudes[state] =
simulator->Amplitude(state);
465 if (!qubitsMapOnHost.empty()) {
466 const size_t offsetBase = qubitsMapOnHost.size();
470 std::vector<size_t> simToOrig(n);
471 size_t offset = offsetBase;
473 for (
size_t qbit = 0; qbit < n; ++qbit) {
474 auto pos = qubitsMapOnHost.find(qbit);
475 if (pos != qubitsMapOnHost.end())
476 simToOrig[pos->second] = pos->first;
478 simToOrig[qbit] = offset++;
481 std::vector<std::complex<double>> remapped(dim);
483 for (
size_t sim_state = 0; sim_state < dim; ++sim_state) {
484 size_t orig_state = 0;
485 for (
size_t qbit = 0; qbit < n; ++qbit) {
486 if (sim_state & (1ULL << qbit))
487 orig_state |= (1ULL << simToOrig[qbit]);
489 if (orig_state < dim) remapped[orig_state] = amplitudes[sim_state];
491 amplitudes.swap(remapped);
495 method !=
simulator->GetSimulationType() ||
496 simulator->GetNumberOfQubits() != numQubits))
517 size_t hostId)
override {
518 const auto recreate = recreateIfNeeded;
522 size_t numQubits = 2;
526 numQubits =
simulator->GetNumberOfQubits();
530 struct ScopedRestoreFlag {
532 ScopedRestoreFlag(
bool &f) : flag(f), saved(f) { flag =
false; }
533 ~ScopedRestoreFlag() { flag = saved; }
534 } restoreGuard(recreateIfNeeded);
539 const auto &first = *res.begin();
544 throw std::runtime_error(
545 "ExecuteOnHostProjectOnZero: no simulator available after "
548 const std::complex<double> result =
simulator->ProjectOnZero();
551 method !=
simulator->GetSimulationType() ||
552 simulator->GetNumberOfQubits() != numQubits))
573 size_t shots = 1000)
override {
580 for (
auto q :
distCirc->AffectedQubits()) {
583 <<
"This is a distributed circuit, using entanglement or cutting"
593 if (
distCirc->HasOpsAfterMeasurements() &&
599 distCirc->MoveMeasurementsAndResets();
601 auto method =
simulator->GetSimulationType();
603 const auto saveSimType = simType;
604 const auto saveMethod = method;
639 std::vector<bool> executed;
642 simType, method, executed);
656 !
distCirc->HasOpsAfterMeasurements()) ||
660 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
662 std::mutex resultsMutex;
680 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
682 threadsPool.Resize(nrThreads);
683 threadsPool.SetFinishLimit(shots);
685 uint64_t jobStream = 0;
687 const size_t curCnt = std::min(cntPerThread, shots);
691 auto job = std::make_shared<ExecuteJob<Time>>(
692 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
693 method, resultsMutex);
697 job->curMaxBondDim = &curMaxBondDim;
702 std::stoull(
configuration.GetConfiguration(
"seed")), jobStream++);
703 job->config.SetConfiguration(
"seed", std::to_string(childSeed));
707 job->optSim = optSim->Clone();
708 job->executedGates = executed;
709 if (job->config.IsSet(
"seed"))
710 job->optSim->SetSeed(
711 std::stoull(job->config.GetConfiguration(
"seed")));
714 threadsPool.AddRunJob(std::move(job));
717 threadsPool.WaitForFinish();
720 const size_t curCnt = shots;
722 auto job = std::make_shared<ExecuteJob<Time>>(
723 dcirc, res, curCnt, nrQubits, nrQubits, nrCbitsResults, simType,
724 method, resultsMutex);
728 job->curMaxBondDim = &curMaxBondDim;
733 optSim->SetMultithreading(
true);
734 job->optSim = optSim;
735 job->executedGates = executed;
737 if (
simulator && method == saveMethod && simType == saveSimType) {
740 job->optSim = optSim;
742 optSim->GetNumberOfQubits());
743 job->executedGates.resize(dcirc->size(),
750 lastGpuDevice = job->optSim ? job->optSim->GetGpuDevice() : -1;
751 if (!recreateIfNeeded)
simulator = job->optSim;
778 size_t shots = 1000)
override {
779 if (!circuit || hostId >=
GetNumHosts())
return {};
786 auto mappingCircuit = circuit;
788 mappingCircuit = std::static_pointer_cast<Circuits::Circuit<Time>>(circuit->Clone());
789 mappingCircuit->Optimize();
792 mappingCircuit, hostId, nrQubits, nrCbits,
true);
797 if (nrCbits == 0) nrCbits = nrQubits;
805 if (
distCirc->HasOpsAfterMeasurements() &&
811 distCirc->MoveMeasurementsAndResets();
813 auto method =
simulator->GetSimulationType();
814 const auto saveSimType = simType;
815 const auto saveMethod = method;
844 std::vector<bool> executed;
846 nrCbits, simType, method, executed);
861 !
distCirc->HasOpsAfterMeasurements()) ||
865 nrThreads = std::min(nrThreads, std::max<size_t>(shots, 1ULL));
870 std::mutex resultsMutex;
878 const size_t cntPerThread = std::max<size_t>(shots / nrThreads, 1ULL);
880 threadsPool.Resize(nrThreads);
881 threadsPool.SetFinishLimit(shots);
883 uint64_t jobStream = 0;
885 const size_t curCnt = std::min(cntPerThread, shots);
888 auto job = std::make_shared<ExecuteJob<Time>>(
889 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
894 job->curMaxBondDim = &curMaxBondDim;
899 std::stoull(
configuration.GetConfiguration(
"seed")), jobStream++);
900 job->config.SetConfiguration(
"seed", std::to_string(childSeed));
904 job->optSim = optSim->Clone();
905 job->executedGates = executed;
906 if (job->config.IsSet(
"seed"))
907 job->optSim->SetSeed(
908 std::stoull(job->config.GetConfiguration(
"seed")));
911 threadsPool.AddRunJob(std::move(job));
914 threadsPool.WaitForFinish();
917 const size_t curCnt = shots;
919 auto job = std::make_shared<ExecuteJob<Time>>(
920 dcirc, res, curCnt, nrQubits, nrCbits, nrCbits, simType, method,
925 job->curMaxBondDim = &curMaxBondDim;
930 optSim->SetMultithreading(
true);
931 job->optSim = optSim;
932 job->executedGates = executed;
936 lastGpuDevice = job->optSim ? job->optSim->GetGpuDevice() : -1;
937 if (!recreateIfNeeded)
simulator = job->optSim;
957 if (!circuit)
return 0;
959 size_t distgates = 0;
961 for (
const auto &op : circuit->GetOperations())
1018 size_t nrQubits = 0)
override {
1027 const size_t allocationQubits = nrQubits == 0
1030 .ApplyConfigurationToSimulator(
simulator);
1032 simulator->AllocateQubits(allocationQubits);
1039 "gpu_device", std::to_string(
simulator->GetGpuDevice()));
1043 simulator->GetConfiguration(
"distributed_shard_devices");
1045 simulator->setGrowthFactorGate(growthFactorGate);
1046 simulator->setGrowthFactorSwap(growthFactorSwap);
1047 simulator->SetLookaheadDepth(lookaheadDepth);
1048 simulator->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
1061 void Configure(
const char *key,
const char *value)
override {
1062 if (!key || !value)
return;
1064 if (std::string(
"distributed_host_qubit_indexing") == key) {
1065 const std::string mode(value);
1066 if (mode !=
"auto" && mode !=
"local" && mode !=
"global")
1067 throw std::invalid_argument(
1068 "distributed_host_qubit_indexing must be auto, local or global");
1073 if (std::string(
"distributed_devices") == key ||
1074 std::string(
"gpu_device") == key)
1077 if (std::string(
"max_simulators") == key)
1080 if (std::string(
"gpu_device") == key) {
1145 std::shared_ptr<Schedulers::IScheduler<Time>>
GetScheduler()
const override {
1160 const std::shared_ptr<IHost<Time>>
GetHost(
size_t hostId)
const override {
1161 if (hostId >=
hosts.size())
return nullptr;
1163 return hosts[hostId];
1198 for (
const auto &host :
hosts) res += host->GetNumQubits();
1213 if (hostId >=
hosts.size())
return 0;
1215 return hosts[hostId]->GetNumQubits();
1229 for (
const auto &host :
hosts) res += host->GetNumNetworkEntangledQubits();
1247 if (hostId >=
hosts.size())
return 0;
1249 return hosts[hostId]->GetNumNetworkEntangledQubits();
1263 for (
const auto &host :
hosts) res += host->GetNumClassicalBits();
1280 if (hostId >=
hosts.size())
return 0;
1282 return hosts[hostId]->GetNumClassicalBits();
1321 const std::vector<uint8_t> &packet)
override {
1348 const auto qubits = op->AffectedQubits();
1350 if (qubits.empty())
return true;
1352 size_t firstQubit = qubits[0];
1354 for (
size_t q = 1; q < qubits.size(); ++q)
1374 const auto qubits = op->AffectedQubits();
1376 if (qubits.empty())
return false;
1380 size_t firstQubit = qubits[0];
1383 firstQubit = qubits[q];
1387 for (; q < qubits.size(); ++q)
1408 const auto qubits = op->AffectedQubits();
1410 if (qubits.empty())
return false;
1412 for (
size_t q = 0; q < qubits.size(); ++q)
1430 const auto qubits = op->AffectedQubits();
1431 if (qubits.size() != 2)
return false;
1449 if (!op->IsConditional())
return false;
1451 const auto qubits = op->AffectedQubits();
1453 const std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1454 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1455 const auto &classicalBits = condOp->GetCondition()->GetBitsIndices();
1457 if (qubits.empty() && classicalBits.empty())
1458 throw std::runtime_error(
1459 "No classical bits specified!");
1467 for (
const auto bit : classicalBits)
1485 if (!op->IsConditional())
1486 throw std::runtime_error(
"Operation is not conditional!");
1488 std::shared_ptr<Circuits::IConditionalOperation<Time>> condOp =
1489 std::static_pointer_cast<Circuits::IConditionalOperation<Time>>(op);
1490 const auto classicalBits = condOp->AffectedBits();
1492 if (classicalBits.empty())
1493 throw std::runtime_error(
"No classical bits specified!");
1507 for (
const auto &host :
hosts) {
1508 const bool present1 = host->IsQubitOnHost(qubitId1);
1509 const bool present2 = host->IsQubitOnHost(qubitId2);
1511 if (present1 && present2)
1513 else if (present1 || present2)
1530 for (
const auto &host :
hosts) {
1531 const bool present1 = host->IsClassicalBitOnHost(bitId1);
1532 const bool present2 = host->IsClassicalBitOnHost(bitId2);
1534 if (present1 && present2)
1536 else if (present1 || present2)
1554 size_t bitId)
const override {
1555 for (
const auto &host :
hosts) {
1556 const bool present1 = host->IsQubitOnHost(qubitId);
1557 const bool present2 = host->IsClassicalBitOnHost(bitId);
1559 if (present1 && present2)
1561 else if (present1 || present2)
1578 for (
const auto &host :
hosts)
1579 if (host->IsQubitOnHost(qubitId))
return host->GetId();
1581 return std::numeric_limits<size_t>::max();
1597 for (
const auto &host :
hosts)
1598 if (host->IsEntangledQubitOnHost(qubitId))
return host->GetId();
1600 return std::numeric_limits<size_t>::max();
1629 for (
const auto &host :
hosts)
1630 if (host->IsClassicalBitOnHost(classicalBitId))
return host->GetId();
1632 return std::numeric_limits<size_t>::max();
1644 if (hostId >=
hosts.size())
return std::vector<size_t>();
1646 return hosts[hostId]->GetQubitsIds();
1660 size_t hostId)
const override {
1661 if (hostId >=
hosts.size())
return std::vector<size_t>();
1663 return hosts[hostId]->GetNetworkEntangledQubitsIds();
1676 if (hostId >=
hosts.size())
return std::vector<size_t>();
1678 return hosts[hostId]->GetClassicalBitsIds();
1692 size_t hostId)
const override {
1693 if (hostId >=
hosts.size())
return std::vector<size_t>();
1695 return hosts[hostId]->GetEntangledQubitMeasurementBitIds();
1743 size_t qubitId2)
const override {
1760 throw std::runtime_error(
1761 "Entanglement between hosts is not supported in the simple network");
1776 throw std::runtime_error(
1777 "Entanglement between hosts is not supported in the simple network");
1790 throw std::runtime_error(
1791 "Entanglement between hosts is not supported in the simple network");
1858 else if (val > (
size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads())
1859 val = (size_t)QC::QubitRegisterCalculator<>::GetNumberOfThreads();
1967 std::shared_ptr<INetwork<Time>>
Clone()
const override {
1970 std::vector<Types::qubit_t> qubits(numHosts);
1971 std::vector<size_t> cbits(numHosts);
1973 for (
size_t h = 0; h < numHosts; ++h) {
1979 std::make_shared<SimpleDisconnectedNetwork<Time, Controller>>(qubits,
2011 size_t nrQubits,
size_t nrCbits,
size_t nrResultCbits,
2013 std::vector<bool> &executed,
bool multithreading =
false,
2014 bool dontRunCircuitStart =
false)
override {
2028 std::pair<Simulators::SimulatorType, Simulators::SimulationType>>
2031 const bool checkTensorNetwork =
2047#ifndef NO_QISKIT_AER
2067 if (checkTensorNetwork &&
2073 const long long int maxBondDim =
configuration.GetConfigurationAsInt(
2074 "matrix_product_state_max_bond_dimension");
2079 (nrQubits <= 4 || maxBondDim > 0))
2080 simulatorTypes.emplace_back(
2096#ifndef NO_QISKIT_AER
2109 simulatorTypes.emplace_back(
2116 (nrQubits <= 4 || maxBondDim > 0))
2117 simulatorTypes.emplace_back(
2125 : Simulators::SimulatorsFactory::ResolveGpuDevice();
2126 Simulators::SimulatorsFactory::ScopedGpuDevice gpuDeviceScope(gpuDevice);
2127 const bool hasGpuCandidate = std::any_of(
2129 [](
const auto& candidate) { return candidate.first == Simulators::SimulatorType::kGpuSim; });
2132 throw std::runtime_error(
"Unable to initialize requested GPU device " + std::to_string(gpuDevice));
2141 simulatorTypes.emplace_back(
2152 simulatorTypes.emplace_back(
2174 simulatorTypes.push_back(candidate);
2177 if (simulatorTypes.empty())
2179 else if (simulatorTypes.size() == 1) {
2180 const auto candidateType = simulatorTypes[0].first;
2181 const auto candidateMethod = simulatorTypes[0].second;
2183 std::shared_ptr<Simulators::ISimulator> sim =
2187 simType = candidateType;
2188 method = candidateMethod;
2192 sim->AllocateQubits(nrQubits);
2195 sim->setGrowthFactorGate(growthFactorGate);
2196 sim->setGrowthFactorSwap(growthFactorSwap);
2197 sim->SetLookaheadDepth(lookaheadDepth);
2198 sim->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
2202 sim->AllocateQubits(nrQubits);
2206 if (!dontRunCircuitStart) {
2207 sim->SetMultithreading(
true);
2209 Time>::ExecuteUpToMeasurements(dcirc, nrQubits, nrCbits,
2210 nrResultCbits, sim, executed, &curMaxBondDim);
2223 const double singularValueThreshold =
2225 "matrix_product_state_truncation_threshold");
2227 const std::string truncationMode =
configuration.GetConfiguration(
2228 "matrix_product_state_truncation_mode");
2230 const std::string mpsSample =
configuration.GetConfiguration(
2231 "mps_sample_measure_algorithm");
2233 std::shared_ptr<Simulators::ISimulator> sim =
2235 simulatorTypes, dcirc, counts, nrQubits, nrCbits, nrResultCbits,
2236 simType, method, executed, maxBondDim, singularValueThreshold,
2237 truncationMode, mpsSample,
GetMaxSimulators(), pauliStrings, multithreading);
2240 sim->AllocateQubits(nrQubits);
2243 sim->setGrowthFactorGate(growthFactorGate);
2244 sim->setGrowthFactorSwap(growthFactorSwap);
2245 sim->SetLookaheadDepth(lookaheadDepth);
2246 sim->SetLookaheadDepthWithHeuristic(lookaheadDepthWithHeuristic);
2250 if (!dontRunCircuitStart) {
2251 sim->SetMultithreading(
true);
2253 dcirc, nrQubits, nrCbits, nrResultCbits, sim, executed, &curMaxBondDim);
2262 optimizeInitialQubitsMap = optimize;
2266 return optimizeInitialQubitsMap;
2270 mpsOptimizeSwaps = optimize;
2274 simulator->SetLookaheadDepthWithHeuristic(0);
2281 mpsOptimizationBondDimensionThreshold = threshold;
2284 std::stoull(
simulator->GetConfiguration(
2285 "matrix_product_state_max_bond_dimension")) < threshold) {
2287 simulator->SetLookaheadDepthWithHeuristic(0);
2292 return mpsOptimizationBondDimensionThreshold;
2296 mpsOptimizationQubitsNumberThreshold = threshold;
2300 simulator->SetLookaheadDepthWithHeuristic(0);
2305 return mpsOptimizationQubitsNumberThreshold;
2309 if (depth < 0) depth = std::numeric_limits<int>::max();
2311 lookaheadDepth = depth;
2313 if (
simulator && lookaheadDepth != std::numeric_limits<int>::max()) {
2315 simulator->SetLookaheadDepthWithHeuristic(0);
2322 if (depth < 0) depth = std::numeric_limits<int>::max();
2324 if (depth > lookaheadDepth) depth = lookaheadDepth;
2326 lookaheadDepthWithHeuristic = depth;
2328 if (
simulator && lookaheadDepthWithHeuristic != std::numeric_limits<int>::max())
2329 simulator->SetLookaheadDepthWithHeuristic(depth);
2333 return lookaheadDepthWithHeuristic;
2340 growthFactorSwap = factor;
2346 growthFactorGate = factor;
2363 const auto requested =
configuration.GetConfiguration(
"distributed_devices");
2366 configuration.SetConfiguration(
"distributed_devices", requested);
2370 size_t qubits)
const {
2374 !
configuration.GetConfiguration(
"distributed_devices").empty() ||
2377 auto devices = found->second;
2383 for (
char c : devices)
if (c ==
',') ++count;
2385 for (
size_t n = count; n > 1; n >>= 1) ++bits;
2386 while (count > 1 && bits >= qubits) {
2391 for (
size_t i = 0; i < count; ++i) {
2392 end = devices.find(
',', end);
2393 if (end == std::string::npos)
break;
2394 if (i + 1 < count) ++end;
2396 devices = devices.substr(0, end);
2398 result.SetConfiguration(
"distributed_devices", devices);
2403 std::shared_ptr<Simulators::ISimulator> &sim,
2405 if (sim->GetSimulationType() ==
2407 (optimizeInitialQubitsMap || mpsOptimizeSwaps) &&
2408 sim->SupportsMPSSwapOptimization()) {
2409 if (mpsOptimizationQubitsNumberThreshold <= nrQubits) {
2410 const auto maxBondDimValue =
2411 configuration.GetConfigurationAsInt(
"matrix_product_state_max_bond_dimension");
2413 if (maxBondDimValue <= 0 ||
2414 static_cast<int>(mpsOptimizationBondDimensionThreshold) <= maxBondDimValue) {
2416 dcirc->ConvertForCutting();
2417 auto layers = dcirc->ToMultipleQubitsLayersNoClone();
2423 if (maxBondDimValue > 0)
2426 if (optimizeInitialQubitsMap) {
2428 sim->SetInitialQubitsMap(optimalMap);
2432 dcirc->SetOperations(optCirc->GetOperations());
2434 if (mpsOptimizeSwaps) {
2436 int lookaheadDepthLocal = lookaheadDepth;
2438 if (lookaheadDepthLocal == std::numeric_limits<int>::max()) {
2439 double avgTwoQubitGatesPerLayer = 0.0;
2440 for (
const auto &layer : layers) {
2441 int twoQubitGates = 0;
2442 for (
const auto &op : layer->GetOperations()) {
2443 if (op->AffectedQubits().size() >= 2) {
2447 avgTwoQubitGatesPerLayer += twoQubitGates;
2449 avgTwoQubitGatesPerLayer /= layers.size();
2451 int lookaheadVal =
static_cast<int>(4. * avgTwoQubitGatesPerLayer);
2452 if (lookaheadVal > 15) lookaheadVal = 15;
2454 lookaheadDepthLocal =
2455 layers.size() < 8 || nrQubits <= 10 ? 0
2456 : layers.size() < 15 ?
static_cast<int>(lookaheadVal)
2457 : layers.size() < 25 ?
static_cast<int>(1.5 * lookaheadVal)
2461 int lookaheadHeuristicDepthLocal = lookaheadDepthWithHeuristic;
2463 if (lookaheadHeuristicDepthLocal == std::numeric_limits<int>::max())
2464 lookaheadHeuristicDepthLocal =
2465 layers.size() < 10 || nrQubits <= 10 ? 0
2466 : layers.size() < 20 ? lookaheadDepthLocal - 1
2467 : lookaheadDepthLocal - 2;
2469 if (lookaheadHeuristicDepthLocal < 0)
2470 lookaheadHeuristicDepthLocal = 0;
2472 sim->setGrowthFactorGate(growthFactorGate);
2473 sim->setGrowthFactorSwap(growthFactorSwap);
2474 sim->SetUseOptimalMeetingPosition(
true);
2475 sim->SetLookaheadDepth(lookaheadDepthLocal);
2476 sim->SetLookaheadDepthWithHeuristic(lookaheadHeuristicDepthLocal);
2477 sim->SetUpcomingGates(dcirc->GetOperations());
2496 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2514 const std::unordered_map<Types::qubit_t, Types::qubit_t> &qubitsMap) {
2518 theClassicalState.
Remap(qubitsMap);
2535 const auto &qubitsMap = optimiser->GetReverseQubitsMap();
2555 const std::unordered_map<Types::qubit_t, Types::qubit_t> &bitsMap)
const {
2558 size_t numClassicalBits = 0;
2559 for (
const auto &[q, b] : bitsMap)
2560 if (b >= numClassicalBits) numClassicalBits = b + 1;
2564 for (
const auto &r : res) {
2567 translatedState.
Remap(bitsMap,
false, numClassicalBits);
2568 translatedRes[translatedState.
GetAllBits()] = r.second;
2571 res.swap(translatedRes);
2587 size_t &nrQubits,
size_t &nrCbits,
bool useSeparateSimForHosts =
false) {
2588 qubitsMapOnHost.clear();
2591 if (!circuit)
return {};
2594 std::static_pointer_cast<SimpleHost<Time>>(
GetHost(hostId));
2595 const size_t hostNrQubits = host->GetNumQubits();
2597 std::unordered_map<Types::qubit_t, Types::qubit_t> reverseQubitsMap;
2599 if (!useSeparateSimForHosts) {
2601 size_t mnq = std::numeric_limits<size_t>::max();
2603 size_t mnb = std::numeric_limits<size_t>::max();
2605 for (
const auto &op : circuit->GetOperations()) {
2606 const auto qbits = op->AffectedQubits();
2607 for (
auto q : qbits) {
2608 if (q > mxq) mxq = q;
2609 if (q < mnq) mnq = q;
2611 const auto cbits = op->AffectedBits();
2612 for (
auto b : cbits) {
2613 if (b > mxb) mxb = b;
2614 if (b < mnb) mnb = b;
2618 if (mnq > mxq) mnq = 0;
2619 if (mnb > mxb) mnb = 0;
2621 nrQubits = mxq - mnq + 1;
2622 nrCbits = mxb - mnb + 1;
2623 if (nrCbits < nrQubits) nrCbits = nrQubits;
2625 const size_t startQubit = host->GetStartQubitId();
2627 if (mnq < startQubit || mxq >= startQubit + hostNrQubits) {
2632 throw std::runtime_error(
"Circuit does not fit on the host!");
2634 for (
size_t i = 0; i < nrCbits; ++i) {
2635 const size_t mapFrom = mnq + i;
2636 const size_t mapTo = startQubit + i;
2638 qubitsMapOnHost[mapFrom] = mapTo;
2639 reverseQubitsMap[mapTo] = mapFrom;
2642 distCirc = std::static_pointer_cast<Circuits::Circuit<Time>>(
2643 circuit->Remap(qubitsMapOnHost, qubitsMapOnHost));
2646 return reverseQubitsMap;
2649 distCirc = circuit->RemapToContinuous(qubitsMapOnHost, reverseQubitsMap,
2657 throw std::runtime_error(
"Circuit does not fit on a host with no qubits!");
2658 const size_t start = host->GetStartQubitId();
2659 bool fitsLocal =
true, fitsGlobal =
true;
2660 for (
const auto& entry : qubitsMapOnHost) {
2661 const auto q = entry.first;
2662 fitsLocal = fitsLocal && q < hostNrQubits;
2663 fitsGlobal = fitsGlobal && q >= start && q - start < hostNrQubits;
2669 if (!(global ? fitsGlobal : fitsLocal))
2670 throw std::runtime_error(
"Circuit does not fit on the host!");
2671 const size_t offset = global ? start : 0;
2673 for (
const auto& pauli : *pauliStrings)
2674 if (pauli.size() > hostNrQubits)
2675 throw std::invalid_argument(
2676 "Host Pauli strings use local indices and must fit the host");
2677 std::unordered_map<Types::qubit_t, Types::qubit_t> restoreQubits;
2678 for (
const auto& [original, compact] : qubitsMapOnHost) {
2679 if (original < offset || original - offset >= hostNrQubits)
2680 throw std::runtime_error(
"Circuit does not fit on the host!");
2681 restoreQubits[compact] = original - offset;
2683 distCirc = std::static_pointer_cast<Circuits::Circuit<Time>>(
2685 qubitsMapOnHost.clear();
2686 for (
size_t q = 0; q < hostNrQubits; ++q) qubitsMapOnHost[q] = q;
2687 nrQubits = hostNrQubits;
2688 }
else if (pauliStrings) {
2692 for (
const auto& pauli : *pauliStrings) width = std::max(width, pauli.size());
2693 for (
size_t q = 0; q < width; ++q)
2694 if (!qubitsMapOnHost.count(q)) qubitsMapOnHost[q] = nrQubits++;
2697 assert(nrQubits == qubitsMapOnHost.size());
2699 if (nrQubits == 0) nrQubits = 1;
2705 throw std::runtime_error(
"Circuit does not fit on the host!");
2707 return reverseQubitsMap;
2722 std::unordered_map<Simulators::SimulatorType, std::string>
2726 GetNumberOfThreads();
2731 std::shared_ptr<Simulators::ISimulator>
2734 std::shared_ptr<Circuits::Circuit<Time>>
2737 std::shared_ptr<IController<Time>>
2742 std::vector<std::shared_ptr<IHost<Time>>>
2745 std::unique_ptr<Estimators::SimulatorsEstimatorInterface<Time>>
2751 bool recreateIfNeeded =
2753 std::unordered_map<Types::qubit_t, Types::qubit_t>
2757 const std::vector<std::string> *pauliStrings =
2761 bool optimizeInitialQubitsMap =
true;
2763 bool mpsOptimizeSwaps =
true;
2764 size_t mpsOptimizationBondDimensionThreshold =
2766 size_t mpsOptimizationQubitsNumberThreshold =
2769 int lookaheadDepth =
2770 std::numeric_limits<int>::max();
2772 int lookaheadDepthWithHeuristic = std::numeric_limits<int>::max();
2775 double growthFactorSwap = 1.;
2776 double growthFactorGate = 0.7;
2777 size_t curMaxBondDim = 0;
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, size_t *curMaxBondDim=nullptr)
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.
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< Simulators::SimulatorType, std::string > resolvedDistributedDevices
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.
Configuration< Time > ExecutionConfiguration(Simulators::SimulatorType type, size_t qubits) const
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
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) override
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.
std::string distributedHostQubitIndexing
Circuits::OperationState classicalState
The classical state of the network.
int GetLastGpuDevice() const override
Get the last used simulator type.
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.
Configuration< Time > configuration
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.
size_t GetCurrentMaxBondDimension() const override
Returns the maximum bond dimension reached.
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 > 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
void CaptureSimulatorConfiguration()
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.
static std::string GpuSvdSettingGroup(const std::string &key)
static int ParseGpuDevice(const std::string &value)
Set a configuration value.
static uint64_t DeriveSeed(uint64_t seed, uint64_t stream)
void setGrowthFactorSwap(double factor)
std::vector< long long int > ComputeOptimalQubitsMap(const std::vector< std::shared_ptr< Circuits::Circuit<> > > &layers, int nrShuffles=0, int nrSwaps=0)
void setGrowthFactorGate(double factor)
void SetMaxBondDimension(IndexType val)
static std::shared_ptr< ISimulator > CreateSimulator(SimulatorType t=SimulatorType::kQCSim, SimulationType method=SimulationType::kMatrixProductState)
Create a quantum computing simulator.
static bool IsGpuLibraryAvailable(int=-1)
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
@ kDistGpuSim
state distributed across local GPUs
@ kGpuSim
gpu simulator type
bool IsDistributedGpuSimulator(SimulatorType type)
bool IsGpuSimulator(SimulatorType type)
double time_type
The type of time.
A way to pack together a circuit and the number of shots for its execution.