22#define _USE_MATH_DEFINES
55 using BitMapping = std::unordered_map<Types::qubit_t, Types::qubit_t>;
63 using value_type =
typename OperationsVector::value_type;
65 using pointer =
typename OperationsVector::pointer;
67 using reference =
typename OperationsVector::reference;
69 using size_type =
typename OperationsVector::size_type;
72 using iterator =
typename OperationsVector::iterator;
113 void ExecuteBD(
const std::shared_ptr<Simulators::ISimulator> &sim,
OperationState &state,
size_t *curMaxBondDim =
nullptr)
const
119 for (
const auto &op : operations)
120 ExecuteOperation(op, sim, state, curMaxBondDim);
121 if (sim->IsGateFusionEnabled())
122 sim->FlushPendingGates();
124 *curMaxBondDim = std::max(*curMaxBondDim, sim->GetExecutedMaxBondDimension());
148 operations.push_back(op);
171 if (index >= operations.size())
173 operations[index] = op;
197 operations.insert(operations.end(), ops.begin(), ops.end());
243 for (
auto &op : operations)
244 newops.emplace_back(op->Clone());
246 return std::make_shared<Circuit<Time>>(newops);
254 for (
const auto &op : operations)
274 std::shared_ptr<Circuit<Time>>
CloneForExecution(uint64_t stream, uint64_t defaultSeed = 0)
const
277 newops.reserve(operations.size());
278 for (
size_t i = 0; i < operations.size(); ++i)
280 const auto &op = operations[i];
281 switch (op->GetType())
287 auto copy = std::static_pointer_cast<ConditionalRandomGen<Time>>(op->Clone());
290 newops.push_back(std::move(copy));
297 newops.push_back(op);
301 return std::make_shared<Circuit<Time>>(newops);
315 for (
auto &op : operations)
316 newops.push_back(op);
318 return std::make_shared<Circuit<Time>>(newops);
337 retained.reserve(operations.size());
339 std::unordered_set<Types::qubit_t> activeQubits;
340 std::unordered_set<size_t> neededBits;
342 for (
auto it = operations.rbegin(); it != operations.rend(); ++it)
344 const auto &op = *it;
348 const auto type = op->GetType();
351 std::shared_ptr<MeasurementOperation<Time>> meas;
352 std::shared_ptr<ConditionalMeasurement<Time>> condMeas;
356 meas = std::static_pointer_cast<MeasurementOperation<Time>>(op);
360 condMeas = std::static_pointer_cast<ConditionalMeasurement<Time>>(op);
361 meas = std::static_pointer_cast<MeasurementOperation<Time>>(condMeas->GetOperation());
367 const auto &qubits = meas->GetQubits();
368 const auto &bits = meas->GetBitsIndices();
369 const size_t count = std::min(qubits.size(), bits.size());
373 std::vector<bool> keepFlags(count,
false);
374 size_t keptCount = 0;
376 for (
size_t idx = count; idx-- > 0;)
378 const auto q = qubits[idx];
379 const auto b = bits[idx];
380 if (activeQubits.count(q) > 0 || neededBits.count(b) > 0)
382 keepFlags[idx] =
true;
394 const auto condBits = condMeas->AffectedBits();
395 neededBits.insert(condBits.begin(), condBits.end());
398 if (keptCount == count)
400 retained.push_back(op);
404 std::vector<std::pair<Types::qubit_t, size_t>> keptPairs;
405 keptPairs.reserve(keptCount);
406 for (
size_t i = 0; i < count; ++i)
409 keptPairs.emplace_back(qubits[i], bits[i]);
412 auto filteredMeas = std::make_shared<MeasurementOperation<Time>>(keptPairs, meas->GetDelay());
413 if (meas->HasReadout())
415 const auto &rates = meas->GetReadout();
416 std::vector<ReadoutRates> keptRates;
417 keptRates.reserve(keptCount);
418 for (
size_t i = 0; i < count; ++i)
421 keptRates.push_back(i < rates.size() ? rates[i] :
ReadoutRates{});
423 filteredMeas->SetReadout(std::move(keptRates));
432 retained.push_back(std::move(filteredMeas));
438 retained.push_back(op);
445 const auto qs = op->AffectedQubits();
446 activeQubits.insert(qs.begin(), qs.end());
450 const auto qs = op->AffectedQubits();
451 activeQubits.insert(qs.begin(), qs.end());
452 const auto bs = op->AffectedBits();
453 neededBits.insert(bs.begin(), bs.end());
457 const auto bs = op->AffectedBits();
458 for (
const auto b : bs)
463 const auto bs = op->AffectedBits();
464 neededBits.insert(bs.begin(), bs.end());
468 const auto qs = op->AffectedQubits();
469 activeQubits.insert(qs.begin(), qs.end());
470 const auto bs = GetReferencedBits(op);
471 neededBits.insert(bs.begin(), bs.end());
482 std::reverse(retained.begin(), retained.end());
483 return std::make_shared<Circuit<Time>>(retained);
500 for (
const auto &op : operations)
501 newops.emplace_back(op->Remap(qubitsMap, bitsMap));
503 return std::make_shared<Circuit<Time>>(newops);
529 std::vector<Types::qubit_t> usedQubits;
530 for (
const auto &op : operations)
531 for (
const auto qubit : op->AffectedQubits())
532 usedQubits.push_back(qubit);
533 std::sort(usedQubits.begin(), usedQubits.end());
534 usedQubits.erase(std::unique(usedQubits.begin(), usedQubits.end()), usedQubits.end());
535 for (
const auto qubit : usedQubits)
536 if (newQubitsMap.find(qubit) == newQubitsMap.end())
538 newQubitsMap[qubit] = nrQubits;
542 for (
const auto &op : operations)
544 const auto affectedBits = GetReferencedBits(op);
546 for (
const auto bit : affectedBits)
548 const auto it = newBitsMap.find(bit);
549 if (it == newBitsMap.end())
551 newBitsMap[bit] = nrCbits;
552 reverseBitsMap[nrCbits] = bit;
557 newops.emplace_back(op->Remap(newQubitsMap, newBitsMap));
560 return std::make_shared<Circuit<Time>>(newops);
581 if (!ignoreNotMapped && sz == 0)
583 for (
const auto &[from, to] : bitsMap)
590 for (
const auto &res : results)
594 mappedState.Remap(bitsMap, ignoreNotMapped, ignoreNotMapped ? bitsMap.size() : sz);
595 newResults[mappedState.GetAllBits()] += res.second;
611 for (
const auto &res : newResults)
612 results[res.first] += res.second;
630 bool ignoreNotMapped =
true,
size_t sz = 0)
632 if (!ignoreNotMapped && sz == 0)
634 for (
const auto &[from, to] : bitsMap)
641 for (
const auto &res : newResults)
645 mappedState.Remap(bitsMap, ignoreNotMapped, ignoreNotMapped ? bitsMap.size() : sz);
646 results[mappedState.GetAllBits()] += res.second;
676 ReplaceThreeQubitAndSwapGates();
688 ReplaceThreeQubitAndSwapGates(
true);
716 for (
size_t i = 0; i < operations.size(); ++i)
718 const auto op = operations[i];
721 newops.emplace_back(op);
727 std::unordered_set<size_t> bits;
728 std::unordered_map<size_t, Types::qubit_t> measQubits;
729 std::unordered_map<size_t, Time> measDelays;
731 std::unordered_map<size_t, ReadoutRates> measReadout;
733 auto affectedBits = op->AffectedBits();
734 auto affectedQubits = op->AffectedQubits();
736 const auto firstMeas = std::static_pointer_cast<MeasurementOperation<Time>>(op);
737 const auto &firstRates = firstMeas->GetReadout();
739 for (
size_t q = 0; q < affectedQubits.size(); ++q)
741 bits.insert(affectedBits[q]);
742 measQubits[affectedBits[q]] = affectedQubits[q];
743 measDelays[affectedBits[q]] = op->GetDelay();
744 if (q < firstRates.size())
745 measReadout[affectedBits[q]] = firstRates[q];
749 for (; j < operations.size(); ++j)
751 const auto op2 = operations[j];
756 affectedQubits = op2->AffectedQubits();
758 const auto meas = std::static_pointer_cast<MeasurementOperation<Time>>(op2);
759 affectedBits = meas->GetBitsIndices();
760 const auto &rates = meas->GetReadout();
761 for (
size_t q = 0; q < affectedBits.size(); ++q)
763 bits.insert(affectedBits[q]);
764 measQubits[affectedBits[q]] = affectedQubits[q];
765 measDelays[affectedBits[q]] = op2->GetDelay();
766 if (q < rates.size())
767 measReadout[affectedBits[q]] = rates[q];
769 measReadout.erase(affectedBits[q]);
777 for (; j < operations.size(); ++j)
779 const auto op2 = operations[j];
783 auto condop = std::static_pointer_cast<IConditionalOperation<Time>>(op2);
784 const auto condbits = condop->AffectedBits();
785 for (
const auto bit : condbits)
786 if (bits.find(bit) != bits.end())
788 auto rebuilt = std::make_shared<MeasurementOperation<Time>>(std::vector{std::make_pair(measQubits[bit], bit)}, measDelays[bit]);
789 const auto rateit = measReadout.find(bit);
790 if (rateit != measReadout.end())
791 rebuilt->SetReadout({rateit->second});
792 newops.emplace_back(rebuilt);
801 for (
auto bit : bits)
803 auto rebuilt = std::make_shared<MeasurementOperation<Time>>(std::vector{std::make_pair(measQubits[bit], bit)}, measDelays[bit]);
804 const auto rateit = measReadout.find(bit);
805 if (rateit != measReadout.end())
806 rebuilt->SetReadout({rateit->second});
807 newops.emplace_back(rebuilt);
811 operations.swap(newops);
824 for (
const auto &op : operations)
826 const auto qbits = op->AffectedQubits();
844 size_t mn = std::numeric_limits<size_t>::max();
845 for (
const auto &op : operations)
847 const auto qbits = op->AffectedQubits();
866 for (
const auto &op : operations)
868 const auto cbits = GetReferencedBits(op);
886 size_t mn = std::numeric_limits<size_t>::max();
887 for (
const auto &op : operations)
889 const auto cbits = GetReferencedBits(op);
909 std::set<size_t> qubits;
910 for (
const auto &op : operations)
912 const auto qbits = op->AffectedQubits();
913 qubits.insert(qbits.begin(), qbits.end());
928 std::set<size_t> cbits;
929 for (
const auto &op : operations)
931 const auto bits = GetReferencedBits(op);
932 cbits.insert(bits.begin(), bits.end());
949 qubitsVec.reserve(qubits.size());
951 for (
auto q : qubits)
952 qubitsVec.emplace_back(q);
967 std::vector<size_t> bitsVec;
968 bitsVec.reserve(bits.size());
971 bitsVec.emplace_back(b);
987 for (
const auto &op : operations)
988 if (op->NeedsEntanglementForDistribution())
1003 for (
const auto &op : operations)
1004 if (op->CanAffectQuantumState())
1019 std::unordered_map<size_t, OperationPtr> lastOps;
1021 for (
const auto &op : operations)
1023 const auto qbits = op->AffectedQubits();
1024 for (
auto q : qbits)
1040 std::unordered_map<size_t, OperationPtr> firstOps;
1042 for (
const auto &op : operations)
1044 const auto qbits = op->AffectedQubits();
1045 for (
auto q : qbits)
1047 if (firstOps.find(q) == firstOps.end())
1067 for (
const auto &[q, op] : GetLastOps)
1085 for (
const auto &[q, op] : GetFirstOps)
1160 std::vector<std::shared_ptr<IOperation<Time>>> newops;
1161 newops.reserve(operations.size());
1163 for (
int i = 0; i < static_cast<int>(operations.size()); ++i)
1165 const std::shared_ptr<IOperation<Time>> &op = operations[i];
1167 const auto type = op->GetType();
1172 std::shared_ptr<IQuantumGate<Time>> gate = std::static_pointer_cast<IQuantumGate<Time>>(op);
1173 const auto qubits = gate->AffectedQubits();
1175 if (qubits.size() == 1)
1179 auto gateType = gate->GetGateType();
1180 bool replace =
false;
1194 if (!optimizeRotationGates)
1196 newops.push_back(op);
1241 for (
size_t j = i + 1; j < operations.size(); ++j)
1243 auto &nextOp = operations[j];
1244 if (!nextOp->CanAffectQuantumState())
1247 const auto nextQubits = nextOp->AffectedQubits();
1248 bool hasQubit =
false;
1250 for (
auto q : nextQubits)
1260 else if (nextQubits.size() != 1)
1264 const auto nextType = nextOp->GetType();
1268 const auto &nextGate = std::static_pointer_cast<SingleQubitGate<Time>>(nextOp);
1269 if (nextGate->GetGateType() == gateType)
1273 const auto params1 = gate->GetParams();
1274 const auto params2 = nextGate->GetParams();
1276 const double param = params1[0] + params2[0];
1277 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1280 newops.push_back(std::make_shared<
PhaseGate<Time>>(qubits[0], param, delay));
1282 newops.push_back(std::make_shared<
RxGate<Time>>(qubits[0], param, delay));
1284 newops.push_back(std::make_shared<
RyGate<Time>>(qubits[0], param, delay));
1286 newops.push_back(std::make_shared<
RzGate<Time>>(qubits[0], param, delay));
1288 nextOp = std::make_shared<NoOperation<Time>>();
1299 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1300 newops.push_back(std::make_shared<
ZGate<Time>>(qubits[0], delay));
1301 nextOp = std::make_shared<NoOperation<Time>>();
1312 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1313 newops.push_back(std::make_shared<
XGate<Time>>(qubits[0], delay));
1314 nextOp = std::make_shared<NoOperation<Time>>();
1323 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1324 newops.push_back(std::make_shared<
SdgGate<Time>>(qubits[0], delay));
1325 nextOp = std::make_shared<NoOperation<Time>>();
1334 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1335 newops.push_back(std::make_shared<
SGate<Time>>(qubits[0], delay));
1336 nextOp = std::make_shared<NoOperation<Time>>();
1345 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1348 param2 = 0.5 * M_PI;
1350 param2 = -0.5 * M_PI;
1352 param2 = 0.25 * M_PI;
1354 param2 = -0.25 * M_PI;
1356 const auto param = gate->GetParams()[0] + param2;
1357 newops.push_back(std::make_shared<
PhaseGate<Time>>(qubits[0], param, delay));
1358 nextOp = std::make_shared<NoOperation<Time>>();
1367 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1370 param1 = -0.5 * M_PI;
1372 param1 = 0.5 * M_PI;
1374 param1 = -0.25 * M_PI;
1376 param1 = 0.25 * M_PI;
1378 const auto param = nextGate->GetParams()[0] + param1;
1379 newops.push_back(std::make_shared<
PhaseGate<Time>>(qubits[0], param, delay));
1380 nextOp = std::make_shared<NoOperation<Time>>();
1391 newops.push_back(op);
1396 newops.push_back(op);
1400 else if (qubits.size() == 2)
1402 auto gateType = gate->GetGateType();
1403 bool replace =
false;
1417 if (!optimizeRotationGates)
1419 newops.push_back(op);
1450 for (
size_t j = i + 1; j < operations.size(); ++j)
1452 auto &nextOp = operations[j];
1453 if (!nextOp->CanAffectQuantumState())
1456 const auto nextQubits = nextOp->AffectedQubits();
1458 bool hasQubit =
false;
1460 for (
auto q : nextQubits)
1461 if (q == qubits[0] || q == qubits[1])
1470 else if (nextQubits.size() != 2)
1475 (qubits[0] == nextQubits[1] && qubits[1] == nextQubits[0])))
1477 else if (!(qubits[0] == nextQubits[0] && qubits[1] == nextQubits[1]))
1480 const auto nextType = nextOp->GetType();
1484 const auto &nextGate = std::static_pointer_cast<TwoQubitsGate<Time>>(nextOp);
1485 if (nextGate->GetGateType() == gateType)
1489 const auto params1 = gate->GetParams();
1490 const auto params2 = nextGate->GetParams();
1491 const double param = params1[0] + params2[0];
1492 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1495 newops.push_back(std::make_shared<
CPGate<Time>>(qubits[0], qubits[1], param, delay));
1497 newops.push_back(std::make_shared<
CRxGate<Time>>(qubits[0], qubits[1], param, delay));
1499 newops.push_back(std::make_shared<
CRyGate<Time>>(qubits[0], qubits[1], param, delay));
1501 newops.push_back(std::make_shared<
CRzGate<Time>>(qubits[0], qubits[1], param, delay));
1503 nextOp = std::make_shared<NoOperation<Time>>();
1516 newops.push_back(op);
1521 newops.push_back(op);
1525 else if (qubits.size() == 3)
1527 auto gateType = gate->GetGateType();
1541 for (
size_t j = i + 1; j < operations.size(); ++j)
1543 auto &nextOp = operations[j];
1544 if (!nextOp->CanAffectQuantumState())
1547 const auto nextQubits = nextOp->AffectedQubits();
1549 bool hasQubit =
false;
1551 for (
auto q : nextQubits)
1552 if (q == qubits[0] || q == qubits[1] || q == qubits[2])
1561 else if (nextQubits.size() != 3)
1566 (qubits[0] != nextQubits[0] || !((qubits[1] == nextQubits[1] && qubits[2] == nextQubits[2]) ||
1567 (qubits[1] == nextQubits[2] && qubits[2] == nextQubits[1]))))
1570 (qubits[2] != nextQubits[2] || !(qubits[1] == nextQubits[1] && qubits[2] == nextQubits[2]) ||
1571 !(qubits[1] == nextQubits[2] && qubits[2] == nextQubits[1])))
1574 const auto nextType = nextOp->GetType();
1578 const auto &nextGate = std::static_pointer_cast<ThreeQubitsGate<Time>>(nextOp);
1579 if (nextGate->GetGateType() == gateType)
1581 nextOp = std::make_shared<NoOperation<Time>>();
1592 newops.push_back(op);
1597 newops.push_back(op);
1602 newops.push_back(op);
1605 newops.push_back(op);
1608 operations.swap(newops);
1625 newops.reserve(operations.size());
1629 std::unordered_map<Types::qubit_t, std::vector<OperationPtr>> qubitOps;
1631 std::vector<OperationPtr> lastOps(qubitsNo);
1633 std::unordered_map<OperationPtr, std::unordered_set<OperationPtr>> dependenciesMap;
1635 for (
const auto &op : operations)
1637 std::unordered_set<OperationPtr> dependencies;
1639 const auto cbits = GetReferencedBits(op);
1640 for (
auto c : cbits)
1642 const auto lastOp = lastOps[c];
1644 dependencies.insert(lastOp);
1647 const auto qubits = op->AffectedQubits();
1648 for (
auto q : qubits)
1650 qubitOps[q].push_back(op);
1652 const auto lastOp = lastOps[q];
1654 dependencies.insert(lastOp);
1659 for (
auto c : cbits)
1662 dependenciesMap[op] = dependencies;
1666 std::vector<Types::qubit_t> indices(qubitsNo, 0);
1668 while (!dependenciesMap.empty())
1674 for (
size_t q = 0; q < qubitsNo; ++q)
1676 if (qubitOps.find(q) == qubitOps.end())
1680 const auto &ops = qubitOps[q];
1681 const auto &op = ops[indices[q]];
1686 bool hasDependencies =
false;
1688 for (
const auto &opd : dependenciesMap[op])
1689 if (dependenciesMap.find(opd) != dependenciesMap.end())
1691 hasDependencies =
true;
1695 if (!hasDependencies)
1705 dependenciesMap.erase(nextOp);
1708 for (
auto q : qubits)
1711 if (indices[q] >= qubitOps[q].
size())
1715 newops.emplace_back(std::move(nextOp));
1722 if (qubitOps.find(q) == qubitOps.end())
1726 const auto &ops = qubitOps[q];
1727 const auto &op = ops[indices[q]];
1729 bool hasDependencies =
false;
1731 for (
const auto &opd : dependenciesMap[op])
1732 if (dependenciesMap.find(opd) != dependenciesMap.end())
1734 hasDependencies =
true;
1738 if (!hasDependencies)
1747 dependenciesMap.erase(nextOp);
1750 for (
auto q : qubits)
1753 if (indices[q] >= qubitOps[q].
size())
1757 newops.emplace_back(std::move(nextOp));
1761 assert(newops.size() == operations.size());
1763 operations.swap(newops);
1775 std::pair<std::vector<size_t>, std::vector<Time>>
GetDepth()
const
1781 std::vector<Time> qubitTimes(qubitsNo, 0);
1782 std::vector<size_t> qubitDepths(qubitsNo, 0);
1784 std::unordered_map<size_t, size_t> fromQubits;
1786 for (
const auto &op : operations)
1788 const auto qbits = op->AffectedQubits();
1789 const auto delay = op->GetDelay();
1793 for (
auto q : qbits)
1795 qubitTimes[q] += delay;
1797 if (qubitTimes[q] > maxTime)
1798 maxTime = qubitTimes[q];
1799 if (qubitDepths[q] > maxDepth)
1800 maxDepth = qubitDepths[q];
1803 const auto t = op->GetType();
1804 std::vector<size_t> condbits;
1812 condbits = op->AffectedBits();
1814 for (
auto bit : condbits)
1816 if (fromQubits.find(bit) != fromQubits.end())
1817 bit = fromQubits[bit];
1820 for (
auto q : qbits)
1828 if (found || bit >= qubitsNo)
1831 qubitTimes[bit] += delay;
1833 if (qubitTimes[bit] > maxTime)
1834 maxTime = qubitTimes[bit];
1835 if (qubitDepths[bit] > maxDepth)
1836 maxDepth = qubitDepths[bit];
1841 const auto condMeas = std::static_pointer_cast<ConditionalMeasurement<Time>>(op);
1842 const auto meas = condMeas->GetOperation();
1843 const auto measQubits = meas->AffectedQubits();
1844 const auto measBits = meas->AffectedBits();
1846 for (
size_t i = 0; i < measQubits.size(); ++i)
1848 if (i < measBits.size())
1849 fromQubits[measBits[i]] = measQubits[i];
1851 fromQubits[measQubits[i]] = measQubits[i];
1857 condbits = op->AffectedBits();
1859 for (
size_t i = 0; i < qbits.size(); ++i)
1861 if (i < condbits.size())
1862 fromQubits[condbits[i]] = qbits[i];
1864 fromQubits[qbits[i]] = qbits[i];
1868 for (
auto q : qbits)
1870 qubitTimes[q] = maxTime;
1871 qubitDepths[q] = maxDepth;
1874 for (
auto bit : condbits)
1876 qubitTimes[bit] = maxTime;
1877 qubitDepths[bit] = maxDepth;
1881 return std::make_pair(qubitDepths, qubitTimes);
1895 auto [qubitDepths, qubitTimes] =
GetDepth();
1898 size_t maxDepth = 0;
1899 for (
size_t qubit = 0; qubit < qubitDepths.size(); ++qubit)
1901 if (qubitTimes[qubit] > maxTime)
1902 maxTime = qubitTimes[qubit];
1903 if (qubitDepths[qubit] > maxDepth)
1904 maxDepth = qubitDepths[qubit];
1907 return std::make_pair(maxDepth, maxTime);
1918 return operations.size();
1931 if (pos >= operations.size())
1934 return operations[pos];
1953 newops.reserve(operations.size());
1955 for (
const auto &op : operations)
1957 const auto qubits = op->AffectedQubits();
1958 bool containsOutsideQubits =
false;
1959 bool containsInsideQubits =
false;
1960 for (
const auto q : qubits)
1962 if (q < startQubit || q > endQubit)
1964 containsOutsideQubits =
true;
1965 if (containsInsideQubits)
1970 containsInsideQubits =
true;
1971 if (containsOutsideQubits)
1976 if (containsInsideQubits)
1978 if (containsOutsideQubits)
1979 throw std::runtime_error(
"Cannot cut the circuit with the specified interval");
1980 newops.emplace_back(op->Clone());
1984 return std::make_shared<Circuit<Time>>(newops);
1989 return std::any_of(operations.begin(), operations.end(), [](
const auto &op) { return op->GetType() == OperationType::kComposite; });
2005 std::unordered_set<Types::qubit_t> measuredQubits;
2006 std::unordered_set<Types::qubit_t> affectedQubits;
2007 std::unordered_set<Types::qubit_t> resetQubits;
2009 for (
const auto &op : operations)
2017 const auto qubits = op->AffectedQubits();
2021 for (
const auto qbit : qubits)
2022 if (resetQubits.find(qbit) != resetQubits.end())
2035 measuredQubits.insert(qubits.begin(), qubits.end());
2043 for (
const auto qbit : qubits)
2049 if (affectedQubits.find(qbit) != affectedQubits.end() || measuredQubits.find(qbit) != measuredQubits.end())
2050 resetQubits.insert(qbit);
2052 affectedQubits.insert(qbit);
2057 for (
const auto qbit : qubits)
2059 if (measuredQubits.find(qbit) != measuredQubits.end())
2062 if (resetQubits.find(qbit) != resetQubits.end())
2066 affectedQubits.insert(qbit);
2074 return !resetQubits.empty();
2098 bool planExecutionOrder = sim && sim->IsGateFusionEnabled() && sim->IsRoutingLookaheadEnabled() && sim->GetGatesCounter() == 0;
2099 std::vector<bool> mask;
2100 if (planExecutionOrder)
2104 planExecutionOrder = std::find(mask.begin(), mask.end(),
true) != mask.end() && std::find(mask.begin(), mask.end(),
false) != mask.end();
2106 if (planExecutionOrder)
2108 const size_t dif = operations.size() - mask.size();
2110 ordered.reserve(operations.size());
2111 for (
size_t i = dif; i < operations.size(); ++i)
2113 ordered.emplace_back(operations[i]);
2114 for (
size_t i = dif; i < operations.size(); ++i)
2116 ordered.emplace_back(operations[i]);
2117 sim->SetUpcomingGates(ordered);
2120 std::vector<bool> executedOps;
2121 executedOps.reserve(operations.size());
2123 std::unordered_set<Types::qubit_t> measuredQubits;
2124 std::unordered_set<Types::qubit_t> affectedQubits;
2126 bool executionStopped =
false;
2128 for (
size_t i = 0; i < operations.size(); ++i)
2130 auto &op = operations[i];
2131 const auto qubits = op->AffectedQubits();
2133 bool executed =
false;
2137 measuredQubits.insert(qubits.begin(), qubits.end());
2143 for (
auto qubit : qubits)
2144 if (affectedQubits.find(qubit) != affectedQubits.end())
2154 ExecuteOperation(op, sim, state, curMaxBondDim);
2158 measuredQubits.insert(qubits.begin(), qubits.end());
2162 const auto bits = op->AffectedBits();
2176 for (
auto bit : bits)
2177 if (measuredQubits.find(bit) != measuredQubits.end())
2183 for (
auto qubit : qubits)
2184 if (measuredQubits.find(qubit) != measuredQubits.end())
2195 ExecuteOperation(op, sim, state, curMaxBondDim);
2203 measuredQubits.insert(bits.begin(), bits.end());
2204 measuredQubits.insert(qubits.begin(), qubits.end());
2208 affectedQubits.insert(qubits.begin(), qubits.end());
2212 executionStopped =
true;
2213 if (sim && sim->IsGateFusionEnabled())
2217 if (!planExecutionOrder)
2218 sim->IncrementGatesCounter();
2225 sim->SetGatesCounter(sim->GetGatesCounter() + 1);
2227 if (executionStopped)
2228 executedOps.emplace_back(executed);
2233 if (sim->IsGateFusionEnabled())
2234 sim->FlushPendingGates();
2236 *curMaxBondDim = std::max(*curMaxBondDim, sim->GetExecutedMaxBondDimension());
2256 size_t *curMaxBondDim =
nullptr)
const
2265 const size_t dif = operations.size() - executedOps.size();
2267 for (
size_t i = dif; i < operations.size(); ++i)
2268 if (!executedOps[i - dif])
2269 ExecuteOperation(operations[i], sim, state, curMaxBondDim);
2271 if (sim->IsGateFusionEnabled())
2272 sim->FlushPendingGates();
2274 *curMaxBondDim = std::max(*curMaxBondDim, sim->GetExecutedMaxBondDimension());
2290 if (executedOps.empty())
2292 executedOps.resize(
size(),
false);
2293 return std::make_shared<Circuit<Time>>(operations);
2297 newops.reserve(operations.size());
2299 const size_t dif = operations.size() - executedOps.size();
2300 for (
size_t i = dif; i < operations.size(); ++i)
2301 if (!executedOps[i - dif])
2302 newops.emplace_back(operations[i]);
2304 std::vector<bool> newExecutedOps(newops.size(),
false);
2305 executedOps.swap(newExecutedOps);
2307 return std::make_shared<Circuit<Time>>(newops);
2312 std::shared_ptr<MeasurementOperation<Time>>
GetLastMeasurements(
const std::vector<bool> &executedOps,
bool sort =
true)
const
2314 const size_t dif = operations.size() - executedOps.size();
2315 std::vector<std::pair<Types::qubit_t, size_t>> measurements;
2316 std::vector<ReadoutRates> readout;
2317 std::unordered_map<size_t, size_t> lastWrite;
2318 bool anyReadout =
false;
2319 measurements.reserve(dif);
2320 readout.reserve(dif);
2322 for (
size_t i = dif; i < operations.size(); ++i)
2325 auto measOp = std::static_pointer_cast<MeasurementOperation<Time>>(operations[i]);
2326 const auto &qubits = measOp->GetQubits();
2327 const auto &bits = measOp->GetBitsIndices();
2328 const auto &rates = measOp->GetReadout();
2330 for (
size_t j = 0; j < qubits.size(); ++j)
2332 lastWrite[bits[j]] = measurements.size();
2333 measurements.emplace_back(qubits[j], bits[j]);
2334 readout.push_back(j < rates.size() ? rates[j] :
ReadoutRates{});
2337 anyReadout = anyReadout || measOp->HasReadout();
2343 std::vector<size_t> order;
2344 order.reserve(lastWrite.size());
2345 for (
size_t i = 0; i < measurements.size(); ++i)
2346 if (lastWrite.at(measurements[i].second) == i)
2351 std::sort(order.begin(), order.end(), [&](
size_t a,
size_t b) { return measurements[a].first < measurements[b].first; });
2353 if (sort || order.size() != measurements.size())
2355 std::vector<std::pair<Types::qubit_t, size_t>> sortedMeas;
2356 std::vector<ReadoutRates> sortedReadout;
2357 sortedMeas.reserve(order.size());
2358 sortedReadout.reserve(order.size());
2359 for (
const auto idx : order)
2361 sortedMeas.push_back(measurements[idx]);
2362 sortedReadout.push_back(readout[idx]);
2364 measurements.swap(sortedMeas);
2365 readout.swap(sortedReadout);
2368 auto merged = std::make_shared<MeasurementOperation<Time>>(measurements);
2370 merged->SetReadout(std::move(readout));
2385 for (
const auto &op : operations)
2408 for (
const auto &op : operations)
2410 const auto qubits = op->AffectedQubits();
2411 if (qubits.size() <= 1)
2414 if (qubits.size() == 2)
2416 if (std::abs(qubits[0] - qubits[1]) != 1)
2424 for (
size_t i = 1; i < qubits.size(); ++i)
2426 if (qubits[i] < minQubit)
2427 minQubit = qubits[i];
2428 else if (qubits[i] > maxQubit)
2429 maxQubit = qubits[i];
2432 if (maxQubit - minQubit >= qubits.size())
2449 std::unordered_map<Types::qubit_t, size_t> qubits;
2450 std::unordered_map<Types::qubit_t, Types::qubits_vector> lastQubits;
2452 for (
const auto &op : operations)
2454 const auto q = op->AffectedQubits();
2460 bool allInTheLastQubits =
true;
2462 for (
const auto qubit : q)
2464 if (lastQubits.find(qubit) == lastQubits.end())
2466 allInTheLastQubits =
false;
2471 const auto &lastQ = lastQubits[qubit];
2473 for (
const auto q1 : q)
2474 if (std::find(lastQ.cbegin(), lastQ.cend(), q1) == lastQ.cend())
2476 allInTheLastQubits =
false;
2480 if (!allInTheLastQubits)
2485 if (allInTheLastQubits)
2488 for (
const auto qubit : q)
2490 if (qubits[qubit] > 1)
2495 lastQubits[qubit] = q;
2513 for (
const auto &op : operations)
2514 if (!op->IsClifford())
2531 size_t cliffordOps = 0;
2532 for (
const auto &op : operations)
2533 if (op->IsClifford())
2536 return static_cast<double>(cliffordOps) / operations.size();
2550 std::unordered_set<Types::qubit_t> cliffordQubits;
2551 std::unordered_set<Types::qubit_t> nonCliffordQubits;
2553 for (
const auto &op : operations)
2555 const auto qubits = op->AffectedQubits();
2556 if (op->IsClifford())
2558 for (
const auto q : qubits)
2559 cliffordQubits.insert(q);
2563 for (
const auto q : qubits)
2564 nonCliffordQubits.insert(q);
2568 for (
const auto q : nonCliffordQubits)
2569 cliffordQubits.erase(q);
2571 return cliffordQubits;
2585 std::vector<std::shared_ptr<Circuit<Time>>> circuits;
2589 std::unordered_map<Types::qubit_t, std::unordered_set<Types::qubit_t>> circuitsMap;
2591 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitCircuitMap;
2596 for (
auto qubit : allQubits)
2598 circuitsMap[qubit] = std::unordered_set<Types::qubit_t>{qubit};
2599 qubitCircuitMap[qubit] = qubit;
2604 for (
const auto &op : operations)
2606 const auto qubits = op->AffectedQubits();
2611 auto qubitIt = qubits.cbegin();
2612 auto firstQubit = *qubitIt;
2614 auto firstQubitCircuit = qubitCircuitMap[firstQubit];
2618 for (; qubitIt != qubits.cend(); ++qubitIt)
2620 auto qubit = *qubitIt;
2623 auto qubitCircuit = qubitCircuitMap[qubit];
2627 if (firstQubitCircuit != qubitCircuit)
2630 circuitsMap[firstQubitCircuit].insert(circuitsMap[qubitCircuit].
begin(), circuitsMap[qubitCircuit].
end());
2633 for (
auto q : circuitsMap[qubitCircuit])
2634 qubitCircuitMap[q] = firstQubitCircuit;
2637 circuitsMap.erase(qubitCircuit);
2642 size_t circSize = 1ULL;
2644 circSize = std::max(circSize,
static_cast<size_t>(circuitsMap.size()));
2645 circuits.resize(circSize);
2647 for (
size_t i = 0; i < circuits.size(); ++i)
2650 std::unordered_map<Types::qubit_t, size_t> qubitsSetsToCircuit;
2652 size_t circuitNo = 0;
2653 for (
const auto &[
id, qubitSet] : circuitsMap)
2655 qubitsSetsToCircuit[id] = circuitNo;
2662 for (
const auto &op : operations)
2664 const auto qubits = op->AffectedQubits();
2668 circuits[0]->AddOperation(op->Clone());
2672 const auto circ = qubitsSetsToCircuit[qubitCircuitMap[*qubits.cbegin()]];
2674 circuits[circ]->AddOperation(op->Clone());
2687 std::vector<std::shared_ptr<Circuits::Circuit<Time>>>
ToLayers()
const
2689 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2692 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2693 std::unordered_map<size_t, size_t> classicalBitLayer;
2696 std::unordered_map<size_t, size_t> classicalBitReadLayer;
2705 if (op->CanAffectQuantumState())
2707 const auto qubits = op->AffectedQubits();
2708 size_t maxLevel = 0;
2713 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2715 if (layers.size() < qubitsUsed[qbit])
2717 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2718 layers.push_back(std::move(circ));
2722 if (op->IsConditional())
2724 const auto bits = op->AffectedBits();
2725 for (
const auto bit : bits)
2726 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2731 for (
const auto bit : op->AffectedBits())
2732 maxLevel = std::max(maxLevel, std::max(classicalBitLayer[bit], classicalBitReadLayer[bit]));
2737 qubitsUsed[qbit] = maxLevel;
2739 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2742 while (layers.size() <= layerIdx)
2745 layers[layerIdx]->AddOperation(op->Clone());
2747 const auto usedBits = op->AffectedBits();
2748 const size_t usedLevel = maxLevel > 0 ? maxLevel : 1;
2749 auto &bitLayers = op->IsConditional() ? classicalBitReadLayer : classicalBitLayer;
2750 for (
const auto bit : usedBits)
2751 bitLayers[bit] = std::max(bitLayers[bit], usedLevel);
2755 layers.back()->AddOperation(op->Clone());
2771 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2774 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2775 std::unordered_map<size_t, size_t> classicalBitLayer;
2778 std::unordered_map<size_t, size_t> classicalBitReadLayer;
2787 if (op->CanAffectQuantumState())
2789 const auto qubits = op->AffectedQubits();
2790 size_t maxLevel = 0;
2795 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2797 if (layers.size() < qubitsUsed[qbit])
2799 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2800 layers.push_back(std::move(circ));
2804 if (op->IsConditional())
2806 const auto bits = op->AffectedBits();
2807 for (
const auto bit : bits)
2808 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2813 for (
const auto bit : op->AffectedBits())
2814 maxLevel = std::max(maxLevel, std::max(classicalBitLayer[bit], classicalBitReadLayer[bit]));
2819 qubitsUsed[qbit] = maxLevel;
2821 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2824 while (layers.size() <= layerIdx)
2827 layers[layerIdx]->AddOperation(op);
2829 const auto usedBits = op->AffectedBits();
2830 const size_t usedLevel = maxLevel > 0 ? maxLevel : 1;
2831 auto &bitLayers = op->IsConditional() ? classicalBitReadLayer : classicalBitLayer;
2832 for (
const auto bit : usedBits)
2833 bitLayers[bit] = std::max(bitLayers[bit], usedLevel);
2837 layers.back()->AddOperation(op);
2855 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2858 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2859 std::unordered_map<size_t, size_t> classicalBitLayer;
2862 std::unordered_map<size_t, size_t> classicalBitReadLayer;
2871 if (op->CanAffectQuantumState())
2873 const auto qubits = op->AffectedQubits();
2874 size_t maxLevel = 0;
2878 if (qubits.size() > 1)
2880 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2882 if (layers.size() < qubitsUsed[qbit])
2884 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2885 layers.push_back(std::move(circ));
2889 if (op->IsConditional())
2891 const auto bits = op->AffectedBits();
2892 for (
const auto bit : bits)
2893 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2898 for (
const auto bit : op->AffectedBits())
2899 maxLevel = std::max(maxLevel, std::max(classicalBitLayer[bit], classicalBitReadLayer[bit]));
2904 qubitsUsed[qbit] = maxLevel;
2906 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2909 while (layers.size() <= layerIdx)
2912 layers[layerIdx]->AddOperation(op->Clone());
2914 const auto usedBits = op->AffectedBits();
2915 const size_t usedLevel = maxLevel > 0 ? maxLevel : 1;
2916 auto &bitLayers = op->IsConditional() ? classicalBitReadLayer : classicalBitLayer;
2917 for (
const auto bit : usedBits)
2918 bitLayers[bit] = std::max(bitLayers[bit], usedLevel);
2922 layers.back()->AddOperation(op->Clone());
2940 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2943 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2948 std::unordered_map<size_t, size_t> classicalBitLayer;
2951 std::unordered_map<size_t, size_t> classicalBitReadLayer;
2960 if (op->CanAffectQuantumState())
2962 const auto qubits = op->AffectedQubits();
2963 size_t maxLevel = 0;
2967 if (qubits.size() > 1)
2969 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2971 if (layers.size() < qubitsUsed[qbit])
2973 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2974 layers.push_back(std::move(circ));
2980 if (op->IsConditional())
2982 const auto bits = op->AffectedBits();
2983 for (
const auto bit : bits)
2984 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2989 for (
const auto bit : op->AffectedBits())
2990 maxLevel = std::max(maxLevel, std::max(classicalBitLayer[bit], classicalBitReadLayer[bit]));
2995 qubitsUsed[qbit] = maxLevel;
2997 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
3000 while (layers.size() <= layerIdx)
3003 layers[layerIdx]->AddOperation(op);
3008 const auto usedBits = op->AffectedBits();
3009 const size_t usedLevel = maxLevel > 0 ? maxLevel : 1;
3010 auto &bitLayers = op->IsConditional() ? classicalBitReadLayer : classicalBitLayer;
3011 for (
const auto bit : usedBits)
3012 bitLayers[bit] = std::max(bitLayers[bit], usedLevel);
3016 layers.back()->AddOperation(op);
3033 auto circuit{std::make_shared<Circuits::Circuit<Time>>()};
3035 for (
const auto &layer : layers)
3036 circuit->AddOperations(layer->GetOperations());
3052 if (op->IsBranching())
3066 return operations.begin();
3077 return operations.end();
3088 return operations.begin();
3099 return operations.end();
3110 return operations.cbegin();
3121 return operations.cend();
3132 return operations.rbegin();
3143 return operations.rend();
3154 return operations.crbegin();
3165 return operations.crend();
3176 return operations.size();
3187 return operations.empty();
3199 return operations[pos];
3211 return operations[pos];
3223 if (
size < operations.size())
3224 operations.resize(
size);
3231 static std::vector<size_t> GetReferencedBits(
const OperationPtr &op)
3233 auto bits = op->AffectedBits();
3234 if (op->IsConditional())
3236 const auto conditional = std::static_pointer_cast<IConditionalOperation<Time>>(op);
3237 if (
const auto operation = conditional->GetOperation())
3239 const auto outputs = GetReferencedBits(operation);
3240 bits.insert(bits.end(), outputs.begin(), outputs.end());
3246 static void ExecuteOperation(
const OperationPtr &op,
const std::shared_ptr<Simulators::ISimulator> &sim, OperationState &state,
size_t *curMaxBondDim)
3249 const auto sourcePosition = sim->GetGatesCounter();
3251 sim->FlushPendingGates();
3256 const auto circuit = std::static_pointer_cast<Circuit<Time>>(op);
3257 for (
const auto &nested : circuit->GetOperations())
3258 ExecuteOperation(nested, sim, state, curMaxBondDim);
3261 sim->FlushPendingGates();
3262 sim->SetGatesCounter(sourcePosition + 1);
3266 op->Execute(sim, state);
3271 sim->FlushPendingGates();
3272 sim->SetGatesCounter(sourcePosition + 1);
3276 const auto bondDim = sim->GetExecutedMaxBondDimension();
3277 if (bondDim > *curMaxBondDim)
3278 *curMaxBondDim = bondDim;
3292 void ReplaceThreeQubitAndSwapGates(
bool onlyThreeQubits =
false)
3302 std::vector<std::shared_ptr<IOperation<Time>>> newops;
3303 newops.reserve(operations.size());
3309 std::shared_ptr<IQuantumGate<Time>> gate = std::static_pointer_cast<IQuantumGate<Time>>(op);
3311 if (NeedsConversion(gate, onlyThreeQubits))
3313 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates = ConvertGate(gate, onlyThreeQubits);
3314 newops.insert(newops.end(), newgates.begin(), newgates.end());
3317 newops.push_back(op);
3321 std::shared_ptr<ConditionalGate<Time>> condgate = std::static_pointer_cast<ConditionalGate<Time>>(op);
3322 std::shared_ptr<IQuantumGate<Time>> gate = std::static_pointer_cast<IQuantumGate<Time>>(condgate->GetOperation());
3324 if (NeedsConversion(gate, onlyThreeQubits))
3326 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates = ConvertGate(gate, onlyThreeQubits);
3327 std::shared_ptr<ICondition> cond = condgate->GetCondition();
3329 for (
auto gate : newgates)
3330 newops.push_back(std::make_shared<ConditionalGate<Time>>(gate, cond));
3333 newops.push_back(op);
3336 newops.push_back(op);
3339 operations.swap(newops);
3351 static bool NeedsConversion(
const std::shared_ptr<IQuantumGate<Time>> &gate,
bool onlyThreeQubits =
false)
3353 const bool hasThreeQubits = gate->GetNumQubits() == 3;
3354 if (onlyThreeQubits)
3355 return hasThreeQubits;
3372 static std::vector<std::shared_ptr<IGateOperation<Time>>> ConvertGate(std::shared_ptr<IQuantumGate<Time>> &gate,
bool onlyThreeQubits =
false)
3376 std::vector<std::shared_ptr<IGateOperation<Time>>> newops;
3378 if (gate->GetNumQubits() == 3)
3383 const size_t q1 = gate->GetQubit(0);
3384 const size_t q2 = gate->GetQubit(1);
3385 const size_t q3 = gate->GetQubit(2);
3388 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
3389 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
3390 newops.push_back(std::make_shared<CSxDagGate<Time>>(q2, q3));
3391 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
3392 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
3396 const size_t q1 = gate->GetQubit(0);
3397 const size_t q2 = gate->GetQubit(1);
3398 const size_t q3 = gate->GetQubit(2);
3402 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
3404 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
3405 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
3406 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
3408 newops.push_back(std::make_shared<PhaseGate<Time>>(q2, -M_PI_2));
3410 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
3411 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
3412 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
3414 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
3416 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
3419 newops.push_back(gate);
3424 const size_t q1 = gate->GetQubit(0);
3425 const size_t q2 = gate->GetQubit(1);
3429 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
3430 newops.push_back(std::make_shared<CXGate<Time>>(q2, q1));
3431 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
3434 newops.push_back(gate);
3524 if (approximateParamsCheck)
3527 const auto params2 = std::static_pointer_cast<IQuantumGate<Time>>(rhs.
GetOperations()[i])->GetParams();
3528 if (params1.size() != params2.size())
3531 for (
size_t j = 0; j < params1.size(); ++j)
3532 if (std::abs(params1[j] - params2[j]) > paramsEpsilon)
3552 const auto leftCondition = std::static_pointer_cast<IConditionalOperation<Time>>(
BaseClass::GetOperations()[i])->GetCondition();
3553 const auto rightCondition = std::static_pointer_cast<IConditionalOperation<Time>>(rhs.
GetOperations()[i])->GetCondition();
3554 if (leftCondition->GetBitsIndices() != rightCondition->GetBitsIndices())
3557 const auto leftEqCondition = std::static_pointer_cast<EqualCondition>(leftCondition);
3558 const auto rightEqCondition = std::static_pointer_cast<EqualCondition>(rightCondition);
3559 if (!leftEqCondition || !rightEqCondition)
3562 if (leftEqCondition->GetAllBits() != rightEqCondition->GetAllBits())
3567 const auto rightOp = std::static_pointer_cast<IConditionalOperation<Time>>(rhs.
GetOperations()[i])->GetOperation();
3575 if (leftCircuit != rightCircuit)
3587 const auto right = std::static_pointer_cast<QuantumChannelOperation<Time>>(rhs.
GetOperations()[i]);
3588 if (left->AffectedQubits() != right->AffectedQubits() ||
3589 !left->GetChannel().IsApprox(right->GetChannel(), approximateParamsCheck ? paramsEpsilon : 0.0))
3595 const auto right = std::static_pointer_cast<Circuits::Delay<Time>>(rhs.
GetOperations()[i]);
3596 if (left->GetQubit() != right->GetQubit())
3598 if (approximateParamsCheck)
3600 if (std::abs(left->GetDuration() - right->GetDuration()) > paramsEpsilon)
3603 else if (left->GetDuration() != right->GetDuration())
3630 return !(*
this == rhs);
3641 approximateParamsCheck = check;
3652 return approximateParamsCheck;
3665 paramsEpsilon = eps;
3678 return paramsEpsilon;
3682 bool approximateParamsCheck =
false;
3683 double paramsEpsilon = 1e-8;
Idle / delay operation for quantum circuits.
Circuit operation for an exact local CPTP quantum channel.
The controlled x rotation gate.
The controlled y rotation gate.
The controlled z rotation gate.
Circuit class for holding the sequence of operations.
iterator begin() noexcept
Get the begin iterator for the operations.
typename OperationsVector::const_reverse_iterator const_reverse_iterator
void ConvertForCutting()
Converts the circuit for distributed computing.
typename OperationsVector::reverse_iterator reverse_iterator
void Execute(const std::shared_ptr< Simulators::ISimulator > &sim, OperationState &state) const override
Execute the circuit on the given simulator.
typename OperationsVector::allocator_type allocator_type
std::unordered_map< Types::qubit_t, Types::qubit_t > BitMapping
The (qu)bit mapping for remapping.
const_iterator cbegin() const noexcept
Get the const begin iterator for the operations.
double CliffordPercentage() const
Get the percentage of Clifford operations in the circuit.
auto size() const
Get the number of operations in the circuit.
bool IsClifford() const override
Checks if the circuit is a Clifford circuit.
iterator end() noexcept
Get the end iterator for the operations.
std::unordered_set< Types::qubit_t > GetCliffordQubits() const
Get the qubits that are acted on by Clifford operations.
typename OperationsVector::iterator iterator
void AddOperation(const OperationPtr &op)
Adds an operation to the circuit.
std::set< size_t > GetBits() const
Returns the classical bits affected by the operations.
void Optimize(bool optimizeRotationGates=true)
Circuit optimization.
void EnsureProperOrderForMeasurements()
std::pair< size_t, Time > GetMaxDepth() const
Get max circuit depth.
std::vector< std::shared_ptr< Circuits::Circuit< Time > > > ToMultipleQubitsLayersNoClone() const
Converts the circuit to layers oriented on multiple qubit gates.
std::set< size_t > GetQubits() const
Returns the qubits affected by the operations.
std::vector< OperationPtr > OperationsVector
The vector of operations.
std::vector< bool > ExecuteNonMeasurements(const std::shared_ptr< Simulators::ISimulator > &sim, OperationState &state, size_t *curMaxBondDim=nullptr) const
Execute the non-measurements operations from the circuit on the given simulator.
void AddResetsAtBeginningIfNeeded(Time delay=0)
Add resets at the beginning of the circuit.
std::vector< std::shared_ptr< Circuits::Circuit< Time > > > ToMultipleQubitsLayers() const
Converts the circuit to layers oriented on multiple qubit gates.
void Clear()
Clears the operations from the circuit.
typename OperationsVector::value_type value_type
void ExecuteMeasurements(const std::shared_ptr< Simulators::ISimulator > &sim, OperationState &state, const std::vector< bool > &executedOps, size_t *curMaxBondDim=nullptr) const
Execute the measurement operations from the circuit on the given simulator.
std::shared_ptr< Operation > OperationPtr
The shared pointer to the operation type.
void Delay(Types::qubit_t qubit, Time duration)
Adds a delay (idle) operation on the specified qubit.
typename OperationsVector::reference reference
bool CanAffectQuantumState() const override
Find if the circuit can affect the quantum state.
auto & operator[](size_t pos)
Get the operation at a given position.
typename OperationsVector::difference_type difference_type
std::pair< std::vector< size_t >, std::vector< Time > > GetDepth() const
Get circuit depth.
const_iterator begin() const noexcept
Get the begin iterator for the operations.
std::unordered_map< size_t, OperationPtr > GetLastOperationsOnQubits() const
Returns the last operations on circuit's qubits.
const_reverse_iterator crend() const noexcept
Get the const reverse end iterator for the operations.
std::shared_ptr< MeasurementOperation< Time > > GetLastMeasurements(const std::vector< bool > &executedOps, bool sort=true) const
bool ActsOnlyOnAdjacentQubits() const
Checks if the circuit has only operations that act on adjacent qubits.
bool HasCompositeOperations() const
Circuit(const OperationsVector &ops={})
Construct a new Circuit object.
bool IsBranching() const override
Checks if any operation is a branching one.
typename OperationsVector::pointer pointer
std::unordered_map< std::vector< bool >, size_t > ExecuteResults
The results of the execution of the circuit.
std::shared_ptr< Circuits::Circuit< Time > > RemoveExecutedOperations(std::vector< bool > &executedOps) const
Returns a new circuit with the operations that were not yet executed.
typename OperationsVector::size_type size_type
auto empty() const
Check if the circuit is empty.
OperationPtr Remap(const BitMapping &qubitsMap, const BitMapping &bitsMap={}) const override
Get a shared pointer to a circuit remapped.
static void AccumulateResults(ExecuteResults &results, const ExecuteResults &newResults)
Accumulate the results of a circuit execution to already existing results.
std::shared_ptr< Circuit< Time > > GetCircuitCut(Types::qubit_t startQubit, Types::qubit_t endQubit) const
Get the circuit cut.
void ConvertForDistribution()
Converts the circuit for distributed computing.
Types::qubits_vector AffectedQubits() const override
Returns the affected qubits.
bool HasOpsAfterMeasurements() const
Checks if the circuit has measurements that are followed by operations that affect the measured qubit...
IOperation< Time > Operation
The operation type.
OperationPtr CloneFlyweight() const
Get a shared pointer to a clone of this object, but without cloning the operations.
bool HasConditionalOperations() const
Checks if the circuit has clasically conditional operations.
void AddResetsIfNeeded(Time delay=0)
Add resets at the end of the circuit.
static void AccumulateResultsWithRemapBack(ExecuteResults &results, const ExecuteResults &newResults, const BitMapping &bitsMap={}, bool ignoreNotMapped=true, size_t sz=0)
Accumulate the results of a circuit execution to already existing results with remapping.
bool HasUnseededRandomOperations() const
Whether an execution needs entropy for classical random operations.
const_reverse_iterator crbegin() const noexcept
Get the const reverse begin iterator for the operations.
OperationPtr GetOperation(size_t pos) const
Get an operation at a given position.
const_iterator cend() const noexcept
Get the const end iterator for the operations.
const_iterator end() const noexcept
Get the end iterator for the operations.
void MoveMeasurementsAndResets()
Move the measurements and resets closer to the beginning of the circuit.
std::shared_ptr< Circuit< Time > > RemapToContinuous(BitMapping &newQubitsMap, BitMapping &reverseBitsMap, size_t &nrQubits, size_t &nrCbits) const
Get a shared pointer to a circuit remapped to a continuous interval starting from zero.
typename OperationsVector::const_iterator const_iterator
std::vector< std::shared_ptr< Circuits::Circuit< Time > > > ToLayers() const
Converts the circuit to layers.
bool NeedsEntanglementForDistribution() const override
Find if the circuit needs entanglement for distribution.
std::unordered_map< size_t, OperationPtr > GetFirstOperationsOnQubits() const
Returns the first operations on circuit's qubits.
void ExecuteBD(const std::shared_ptr< Simulators::ISimulator > &sim, OperationState &state, size_t *curMaxBondDim=nullptr) const
Execute the circuit on the given simulator.
reverse_iterator rbegin() noexcept
Get the reverse begin iterator for the operations.
void SetOperations(const OperationsVector &ops)
Set the operations in the circuit.
typename OperationsVector::const_pointer const_pointer
void AddOperations(const OperationsVector &ops)
Adds operations to the circuit.
std::shared_ptr< Circuit< Time > > CloneForExecution(uint64_t stream, uint64_t defaultSeed=0) const
Copy a circuit for one execution job, isolating classical RNGs.
size_t GetMaxQubitIndex() const
Returns the max qubit id for all operations.
OperationPtr Clone() const override
Get a shared pointer to a clone of this object.
size_t GetMaxCbitIndex() const
Returns the max classical bit id for all operations.
void AddCircuit(const std::shared_ptr< Circuit< Time > > &circuit)
Adds operations from another circuit to the circuit.
typename OperationsVector::const_reference const_reference
static ExecuteResults RemapResultsBack(const ExecuteResults &results, const BitMapping &bitsMap={}, bool ignoreNotMapped=false, size_t sz=0)
Map back the results for a remapped circuit.
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.
const auto & operator[](size_t pos) const
Get the operation at a given position.
std::vector< std::shared_ptr< Circuits::Circuit< Time > > > ToLayersNoClone() const
Converts the circuit to layers.
std::shared_ptr< Circuit< Time > > RemoveEndMeasurements() const
Creates a new circuit with terminal measurements removed.
size_t GetMinCbitIndex() const
Returns the min classical bit id for all operations.
size_t GetNumberOfOperations() const
Get the number of operations in the circuit.
std::vector< size_t > AffectedBits() const override
Returns the affected bits.
size_t GetMinQubitIndex() const
Returns the min qubit id for all operations.
void resize(size_t size)
Resizes the circuit.
OperationType GetType() const override
Get the type of the circuit.
void ReplaceOperation(size_t index, const OperationPtr &op)
Replaces an operation in the circuit.
reverse_iterator rend() noexcept
Get the reverse end iterator for the operations.
bool IsForest() const
Checks if the circuit is a forest circuit.
std::vector< std::shared_ptr< Circuit< Time > > > SplitCircuit() const
Splits a circuit that has disjoint subcircuits in it into separate circuits.
const OperationsVector & GetOperations() const
Get the operations in the circuit.
std::shared_ptr< Operation > OperationPtr
The shared pointer to the operation type.
double GetParamsEpsilon() const
Gets the epsilon used for checking approximate equality of gate parameters.
std::vector< OperationPtr > OperationsVector
The vector of operations.
void SetApproximateParamsCheck(bool check)
Sets whether to check approximate equality of gate parameters.
ComparableCircuit & operator=(const BaseClass &circ)
Assignment operator.
bool operator==(const BaseClass &rhs) const
Comparison operator.
bool GetApproximateParamsCheck() const
Gets whether to check approximate equality of gate parameters.
ComparableCircuit(const BaseClass &circ)
Construct a new ComparableCircuit object.
IOperation< Time > Operation
The operation type.
bool operator!=(const BaseClass &rhs) const
Comparison operator.
Circuit< Time > BaseClass
The base class type.
ComparableCircuit(const OperationsVector &ops={})
Construct a new ComparableCircuit object.
void SetParamsEpsilon(double eps)
Sets the epsilon used for checking approximate equality of gate parameters.
A conditional measurement.
A conditional random generator.
Delay (idle) operation class.
std::shared_ptr< IOperation< Time > > GetOperation() const
Get the operation for the conditional operation.
virtual Types::qubits_vector AffectedQubits() const
Returns the affected qubits.
Types::time_type GetDelay() const
IOperation(Types::time_type delay=0)
The interface for quantum gates.
virtual QuantumGateType GetGateType() const =0
Get the type of the quantum gate.
virtual std::vector< double > GetParams() const
Get the gate parameters.
The state class that stores the classical state of a quantum circuit execution.
void Reset(bool value=false)
Set the classical bits with the specified value.
bool HasExplicitSeed() const
std::shared_ptr< Random< Time > > CloneForExecution(uint64_t stream, uint64_t defaultSeed=0) const
Clone the generator for an independent execution stream.
const std::vector< bool > & GetResetTargets() const
Get the values to reset the qubits to.
static uint64_t DeriveSeed(uint64_t seed, uint64_t stream)
OperationType
The type of operations.
@ kConditionalGate
conditional gate, similar with gate, but conditioned on something from 'OperationState'
@ kDelay
a delay or idle period on one or more qubits
@ kNoOp
no operation, just a placeholder, could be used to erase some operation from a circuit
@ kComposite
a composite operation, contains other operations - should not be used in the beginning,...
@ kRandomGen
random classical bit generator, result in 'OperationState'
@ kConditionalRandomGen
conditional random generator, similar with random gen, but conditioned on something from 'OperationSt...
@ kConditionalMeasurement
conditional measurement, similar with measurement, but conditioned on something from 'OperationState'
@ kMeasurement
measurement, result in 'OperationState'
@ kGate
the usual quantum gate, result stays in simulator's state
@ kReset
reset, no result in 'state', just apply measurement, then apply not on all qubits that were measured ...
@ kQuantumChannel
a non-unitary CPTP operation on the simulator state
Classical readout-error rates for one measured qubit.
@ kMatrixProductState
matrix product state simulation type
@ kMatrixProductOperator
Matrix product operator simulation type.
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
uint_fast64_t qubit_t
The type of a qubit.