21#define _USE_MATH_DEFINES
44template <
typename Time = Types::time_type>
48 std::unordered_map<std::vector<bool>,
60 std::vector<OperationPtr>;
62 using value_type =
typename OperationsVector::value_type;
64 using pointer =
typename OperationsVector::pointer;
66 using reference =
typename OperationsVector::reference;
68 using size_type =
typename OperationsVector::size_type;
71 using iterator =
typename OperationsVector::iterator;
75 typename OperationsVector::const_reverse_iterator;
95 void Execute(
const std::shared_ptr<Simulators::ISimulator> &sim,
101 for (
const auto &op : operations)
102 op->Execute(sim, state);
133 if (index >= operations.size())
135 operations[index] = op;
155 operations.insert(operations.end(), ops.begin(), ops.end());
182 void Clear() { operations.clear(); }
193 for (
auto &op : operations)
194 newops.emplace_back(op->Clone());
196 return std::make_shared<Circuit<Time>>(newops);
209 for (
auto &op : operations)
210 newops.push_back(op);
212 return std::make_shared<Circuit<Time>>(newops);
226 const BitMapping &bitsMap = {})
const override {
229 for (
const auto &op : operations)
230 newops.emplace_back(op->Remap(qubitsMap, bitsMap));
232 return std::make_shared<Circuit<Time>>(newops);
249 size_t &nrCbits)
const {
257 for (
const auto &op : operations) {
258 const auto affectedBits = op->AffectedBits();
259 const auto affectedQubits = op->AffectedQubits();
261 for (
const auto qubit : affectedQubits) {
262 const auto it = newQubitsMap.find(qubit);
263 if (it == newQubitsMap.end()) {
264 newQubitsMap[qubit] = nrQubits;
269 for (
const auto bit : affectedBits) {
270 const auto it = newBitsMap.find(bit);
271 if (it == newBitsMap.end()) {
272 newBitsMap[bit] = nrCbits;
273 reverseBitsMap[nrCbits] = bit;
278 newops.emplace_back(op->Remap(newQubitsMap, newBitsMap));
281 return std::make_shared<Circuit<Time>>(newops);
300 bool ignoreNotMapped =
false,
304 if (!ignoreNotMapped && sz == 0) {
305 for (
const auto &[from, to] : bitsMap)
312 for (
const auto &res : results) {
315 mappedState.Remap(bitsMap, ignoreNotMapped,
316 ignoreNotMapped ? bitsMap.size() : sz);
317 newResults[mappedState.GetAllBits()] += res.second;
333 for (
const auto &res : newResults)
334 results[res.first] += res.second;
354 bool ignoreNotMapped =
true,
356 if (!ignoreNotMapped && sz == 0) {
357 for (
const auto &[from, to] : bitsMap)
364 for (
const auto &res : newResults) {
367 mappedState.Remap(bitsMap, ignoreNotMapped,
368 ignoreNotMapped ? bitsMap.size() : sz);
369 results[mappedState.GetAllBits()] += res.second;
398 ReplaceThreeQubitAndSwapGates();
434 for (
size_t i = 0; i < operations.size(); ++i) {
435 const auto op = operations[i];
437 newops.emplace_back(op);
443 std::unordered_set<size_t> bits;
444 std::unordered_map<size_t, Types::qubit_t> measQubits;
445 std::unordered_map<size_t, Time> measDelays;
447 auto affectedBits = op->AffectedBits();
448 auto affectedQubits = op->AffectedQubits();
450 for (
size_t q = 0; q < affectedQubits.size(); ++q) {
451 bits.insert(affectedBits[q]);
452 measQubits[affectedBits[q]] = affectedQubits[q];
453 measDelays[affectedBits[q]] = op->GetDelay();
457 for (; j < operations.size(); ++j) {
458 const auto op2 = operations[j];
463 affectedQubits = op2->AffectedQubits();
466 std::static_pointer_cast<MeasurementOperation<Time>>(op2);
467 affectedBits = meas->GetBitsIndices();
468 for (
size_t q = 0; q < affectedBits.size(); ++q) {
469 bits.insert(affectedBits[q]);
470 measQubits[affectedBits[q]] = affectedQubits[q];
471 measDelays[affectedBits[q]] = op2->GetDelay();
479 for (; j < operations.size(); ++j) {
480 const auto op2 = operations[j];
485 std::static_pointer_cast<IConditionalOperation<Time>>(op2);
486 const auto condbits = condop->AffectedBits();
487 for (
const auto bit : condbits)
488 if (bits.find(bit) != bits.end()) {
490 std::vector{std::make_pair(measQubits[bit], bit)},
500 for (
auto bit : bits)
502 std::vector{std::make_pair(measQubits[bit], bit)},
506 operations.swap(newops);
518 for (
const auto &op : operations) {
519 const auto qbits = op->AffectedQubits();
536 size_t mn = std::numeric_limits<size_t>::max();
537 for (
const auto &op : operations) {
538 const auto qbits = op->AffectedQubits();
556 for (
const auto &op : operations) {
557 const auto cbits = op->AffectedBits();
574 size_t mn = std::numeric_limits<size_t>::max();
575 for (
const auto &op : operations) {
576 const auto cbits = op->AffectedBits();
595 std::set<size_t> qubits;
596 for (
const auto &op : operations) {
597 const auto qbits = op->AffectedQubits();
598 qubits.insert(qbits.begin(), qbits.end());
612 std::set<size_t> cbits;
613 for (
const auto &op : operations) {
614 const auto bits = op->AffectedBits();
615 cbits.insert(bits.begin(), bits.end());
631 qubitsVec.reserve(qubits.size());
633 for (
auto q : qubits)
634 qubitsVec.emplace_back(q);
648 std::vector<size_t> bitsVec;
649 bitsVec.reserve(bits.size());
652 bitsVec.emplace_back(b);
667 for (
const auto &op : operations)
668 if (op->NeedsEntanglementForDistribution())
682 for (
const auto &op : operations)
683 if (op->CanAffectQuantumState())
697 std::unordered_map<size_t, OperationPtr> lastOps;
699 for (
const auto &op : operations) {
700 const auto qbits = op->AffectedQubits();
716 std::unordered_map<size_t, OperationPtr> firstOps;
718 for (
const auto &op : operations) {
719 const auto qbits = op->AffectedQubits();
720 for (
auto q : qbits) {
721 if (firstOps.find(q) == firstOps.end())
740 for (
const auto &[q, op] : GetLastOps)
744 operations.emplace_back(
759 for (
const auto &[q, op] : GetFirstOps)
835 std::vector<std::shared_ptr<IOperation<Time>>> newops;
836 newops.reserve(operations.size());
838 for (
int i = 0; i < static_cast<int>(operations.size()); ++i) {
839 const std::shared_ptr<IOperation<Time>> &op = operations[i];
841 const auto type = op->GetType();
845 std::shared_ptr<IQuantumGate<Time>> gate =
846 std::static_pointer_cast<IQuantumGate<Time>>(op);
847 const auto qubits = gate->AffectedQubits();
849 if (qubits.size() == 1) {
852 auto gateType = gate->GetGateType();
853 bool replace =
false;
866 if (!optimizeRotationGates) {
867 newops.push_back(op);
912 for (
size_t j = i + 1; j < operations.size(); ++j) {
913 auto &nextOp = operations[j];
914 if (!nextOp->CanAffectQuantumState())
917 const auto nextQubits = nextOp->AffectedQubits();
918 bool hasQubit =
false;
920 for (
auto q : nextQubits)
921 if (q == qubits[0]) {
929 else if (nextQubits.size() != 1)
933 const auto nextType = nextOp->GetType();
937 const auto &nextGate =
938 std::static_pointer_cast<SingleQubitGate<Time>>(nextOp);
939 if (nextGate->GetGateType() == gateType) {
941 const auto params1 = gate->GetParams();
942 const auto params2 = nextGate->GetParams();
944 const double param = params1[0] + params2[0];
945 const auto delay = gate->GetDelay() + nextGate->GetDelay();
949 qubits[0], param, delay));
952 qubits[0], param, delay));
955 qubits[0], param, delay));
958 qubits[0], param, delay));
960 nextOp = std::make_shared<NoOperation<Time>>();
965 nextGate->GetGateType() ==
968 nextGate->GetGateType() ==
973 const auto delay = gate->GetDelay() + nextGate->GetDelay();
976 nextOp = std::make_shared<NoOperation<Time>>();
981 nextGate->GetGateType() ==
984 nextGate->GetGateType() ==
989 const auto delay = gate->GetDelay() + nextGate->GetDelay();
992 nextOp = std::make_shared<NoOperation<Time>>();
997 nextGate->GetGateType() ==
1001 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1004 nextOp = std::make_shared<NoOperation<Time>>();
1009 nextGate->GetGateType() ==
1013 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1016 nextOp = std::make_shared<NoOperation<Time>>();
1021 (nextGate->GetGateType() ==
1023 nextGate->GetGateType() ==
1025 nextGate->GetGateType() ==
1027 nextGate->GetGateType() ==
1029 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1032 param2 = 0.5 * M_PI;
1033 else if (nextGate->GetGateType() ==
1035 param2 = -0.5 * M_PI;
1036 else if (nextGate->GetGateType() ==
1038 param2 = 0.25 * M_PI;
1040 param2 = -0.25 * M_PI;
1042 const auto param = gate->GetParams()[0] + param2;
1044 qubits[0], param, delay));
1045 nextOp = std::make_shared<NoOperation<Time>>();
1049 }
else if (nextGate->GetGateType() ==
1055 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1058 param1 = -0.5 * M_PI;
1060 param1 = 0.5 * M_PI;
1062 param1 = -0.25 * M_PI;
1064 param1 = 0.25 * M_PI;
1066 const auto param = nextGate->GetParams()[0] + param1;
1068 qubits[0], param, delay));
1069 nextOp = std::make_shared<NoOperation<Time>>();
1079 newops.push_back(op);
1083 newops.push_back(op);
1086 }
else if (qubits.size() == 2) {
1087 auto gateType = gate->GetGateType();
1088 bool replace =
false;
1101 if (!optimizeRotationGates) {
1102 newops.push_back(op);
1133 for (
size_t j = i + 1; j < operations.size(); ++j) {
1134 auto &nextOp = operations[j];
1135 if (!nextOp->CanAffectQuantumState())
1138 const auto nextQubits = nextOp->AffectedQubits();
1140 bool hasQubit =
false;
1142 for (
auto q : nextQubits)
1143 if (q == qubits[0] || q == qubits[1]) {
1151 else if (nextQubits.size() != 2)
1156 !((qubits[0] == nextQubits[0] &&
1157 qubits[1] == nextQubits[1]) ||
1158 (qubits[0] == nextQubits[1] &&
1159 qubits[1] == nextQubits[0])))
1161 else if (!(qubits[0] == nextQubits[0] &&
1162 qubits[1] == nextQubits[1]))
1165 const auto nextType = nextOp->GetType();
1169 const auto &nextGate =
1170 std::static_pointer_cast<TwoQubitsGate<Time>>(nextOp);
1171 if (nextGate->GetGateType() == gateType) {
1173 const auto params1 = gate->GetParams();
1174 const auto params2 = nextGate->GetParams();
1175 const double param = params1[0] + params2[0];
1176 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1180 qubits[0], qubits[1], param, delay));
1183 qubits[0], qubits[1], param, delay));
1186 qubits[0], qubits[1], param, delay));
1189 qubits[0], qubits[1], param, delay));
1191 nextOp = std::make_shared<NoOperation<Time>>();
1203 newops.push_back(op);
1207 newops.push_back(op);
1210 }
else if (qubits.size() == 3) {
1211 auto gateType = gate->GetGateType();
1224 for (
size_t j = i + 1; j < operations.size(); ++j) {
1225 auto &nextOp = operations[j];
1226 if (!nextOp->CanAffectQuantumState())
1229 const auto nextQubits = nextOp->AffectedQubits();
1231 bool hasQubit =
false;
1233 for (
auto q : nextQubits)
1234 if (q == qubits[0] || q == qubits[1] || q == qubits[2]) {
1242 else if (nextQubits.size() != 3)
1247 (qubits[0] != nextQubits[0] ||
1248 !((qubits[1] == nextQubits[1] &&
1249 qubits[2] == nextQubits[2]) ||
1250 (qubits[1] == nextQubits[2] &&
1251 qubits[2] == nextQubits[1]))))
1254 (qubits[2] != nextQubits[2] ||
1255 !(qubits[1] == nextQubits[1] &&
1256 qubits[2] == nextQubits[2]) ||
1257 !(qubits[1] == nextQubits[2] &&
1258 qubits[2] == nextQubits[1])))
1261 const auto nextType = nextOp->GetType();
1265 const auto &nextGate =
1266 std::static_pointer_cast<ThreeQubitsGate<Time>>(nextOp);
1267 if (nextGate->GetGateType() == gateType) {
1268 nextOp = std::make_shared<NoOperation<Time>>();
1278 newops.push_back(op);
1282 newops.push_back(op);
1286 newops.push_back(op);
1288 newops.push_back(op);
1291 operations.swap(newops);
1303 newops.reserve(operations.size());
1307 std::unordered_map<Types::qubit_t, std::vector<OperationPtr>> qubitOps;
1309 std::vector<OperationPtr> lastOps(qubitsNo);
1311 std::unordered_map<OperationPtr, std::unordered_set<OperationPtr>>
1314 for (
const auto &op : operations) {
1315 std::unordered_set<OperationPtr> dependencies;
1317 const auto cbits = op->AffectedBits();
1318 for (
auto c : cbits) {
1319 const auto lastOp = lastOps[c];
1321 dependencies.insert(lastOp);
1324 const auto qubits = op->AffectedQubits();
1325 for (
auto q : qubits) {
1326 qubitOps[q].push_back(op);
1328 const auto lastOp = lastOps[q];
1330 dependencies.insert(lastOp);
1335 for (
auto c : cbits)
1338 dependenciesMap[op] = dependencies;
1342 std::vector<Types::qubit_t> indices(qubitsNo, 0);
1344 while (!dependenciesMap.empty()) {
1349 for (
size_t q = 0; q < qubitsNo; ++q) {
1350 if (qubitOps.find(q) ==
1355 const auto &ops = qubitOps[q];
1356 const auto &op = ops[indices[q]];
1361 bool hasDependencies =
false;
1363 for (
const auto &opd : dependenciesMap[op])
1364 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1365 hasDependencies =
true;
1369 if (!hasDependencies) {
1377 dependenciesMap.erase(nextOp);
1380 for (
auto q : qubits) {
1382 if (indices[q] >= qubitOps[q].
size())
1386 newops.emplace_back(std::move(nextOp));
1392 if (qubitOps.find(q) ==
1397 const auto &ops = qubitOps[q];
1398 const auto &op = ops[indices[q]];
1400 bool hasDependencies =
false;
1402 for (
const auto &opd : dependenciesMap[op])
1403 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1404 hasDependencies =
true;
1408 if (!hasDependencies) {
1415 dependenciesMap.erase(nextOp);
1418 for (
auto q : qubits) {
1420 if (indices[q] >= qubitOps[q].
size())
1424 newops.emplace_back(std::move(nextOp));
1428 assert(newops.size() == operations.size());
1430 operations.swap(newops);
1442 std::pair<std::vector<size_t>, std::vector<Time>>
GetDepth()
const {
1447 std::vector<Time> qubitTimes(qubitsNo, 0);
1448 std::vector<size_t> qubitDepths(qubitsNo, 0);
1450 std::unordered_map<size_t, size_t> fromQubits;
1452 for (
const auto &op : operations) {
1453 const auto qbits = op->AffectedQubits();
1454 const auto delay = op->GetDelay();
1458 for (
auto q : qbits) {
1459 qubitTimes[q] += delay;
1461 if (qubitTimes[q] > maxTime)
1462 maxTime = qubitTimes[q];
1463 if (qubitDepths[q] > maxDepth)
1464 maxDepth = qubitDepths[q];
1467 const auto t = op->GetType();
1468 std::vector<size_t> condbits;
1476 condbits = op->AffectedBits();
1478 for (
auto bit : condbits) {
1479 if (fromQubits.find(bit) != fromQubits.end())
1480 bit = fromQubits[bit];
1483 for (
auto q : qbits) {
1489 if (found || bit >= qubitsNo)
1492 qubitTimes[bit] += delay;
1494 if (qubitTimes[bit] > maxTime)
1495 maxTime = qubitTimes[bit];
1496 if (qubitDepths[bit] > maxDepth)
1497 maxDepth = qubitDepths[bit];
1501 const auto condMeas =
1502 std::static_pointer_cast<ConditionalMeasurement<Time>>(op);
1503 const auto meas = condMeas->GetOperation();
1504 const auto measQubits = meas->AffectedQubits();
1505 const auto measBits = meas->AffectedBits();
1507 for (
size_t i = 0; i < measQubits.size(); ++i) {
1508 if (i < measBits.size())
1509 fromQubits[measBits[i]] = measQubits[i];
1511 fromQubits[measQubits[i]] = measQubits[i];
1515 condbits = op->AffectedBits();
1517 for (
size_t i = 0; i < qbits.size(); ++i) {
1518 if (i < condbits.size())
1519 fromQubits[condbits[i]] = qbits[i];
1521 fromQubits[qbits[i]] = qbits[i];
1525 for (
auto q : qbits) {
1526 qubitTimes[q] = maxTime;
1527 qubitDepths[q] = maxDepth;
1530 for (
auto bit : condbits) {
1531 qubitTimes[bit] = maxTime;
1532 qubitDepths[bit] = maxDepth;
1536 return std::make_pair(qubitDepths, qubitTimes);
1549 auto [qubitDepths, qubitTimes] =
GetDepth();
1552 size_t maxDepth = 0;
1553 for (
size_t qubit = 0; qubit < qubitDepths.size(); ++qubit) {
1554 if (qubitTimes[qubit] > maxTime)
1555 maxTime = qubitTimes[qubit];
1556 if (qubitDepths[qubit] > maxDepth)
1557 maxDepth = qubitDepths[qubit];
1560 return std::make_pair(maxDepth, maxTime);
1580 if (pos >= operations.size())
1583 return operations[pos];
1602 newops.reserve(operations.size());
1604 for (
const auto &op : operations) {
1605 const auto qubits = op->AffectedQubits();
1606 bool containsOutsideQubits =
false;
1607 bool containsInsideQubits =
false;
1608 for (
const auto q : qubits) {
1609 if (q < startQubit || q > endQubit) {
1610 containsOutsideQubits =
true;
1611 if (containsInsideQubits)
1614 containsInsideQubits =
true;
1615 if (containsOutsideQubits)
1620 if (containsInsideQubits) {
1621 if (containsOutsideQubits)
1622 throw std::runtime_error(
1623 "Cannot cut the circuit with the specified interval");
1624 newops.emplace_back(op->Clone());
1628 return std::make_shared<Circuit<Time>>(newops);
1642 std::unordered_set<Types::qubit_t> measuredQubits;
1643 std::unordered_set<Types::qubit_t> affectedQubits;
1644 std::unordered_set<Types::qubit_t> resetQubits;
1646 for (
const auto &op : operations) {
1647 const auto qubits = op->AffectedQubits();
1650 for (
const auto qbit : qubits)
1651 if (resetQubits.find(qbit) !=
1665 measuredQubits.insert(qubits.begin(), qubits.end());
1673 for (
const auto qbit : qubits) {
1677 if (affectedQubits.find(qbit) != affectedQubits.end() ||
1678 measuredQubits.find(qbit) != measuredQubits.end())
1679 resetQubits.insert(qbit);
1681 affectedQubits.insert(qbit);
1684 for (
const auto qbit : qubits) {
1685 if (measuredQubits.find(qbit) !=
1690 if (resetQubits.find(qbit) !=
1695 affectedQubits.insert(qbit);
1718 std::vector<bool> executedOps;
1719 executedOps.reserve(operations.size());
1721 std::unordered_set<Types::qubit_t> measuredQubits;
1722 std::unordered_set<Types::qubit_t> affectedQubits;
1724 bool executionStopped =
false;
1726 for (
size_t i = 0; i < operations.size(); ++i) {
1727 auto &op = operations[i];
1728 const auto qubits = op->AffectedQubits();
1730 bool executed =
false;
1736 measuredQubits.insert(qubits.begin(), qubits.end());
1741 for (
auto qubit : qubits)
1742 if (affectedQubits.find(qubit) != affectedQubits.end()) {
1749 op->Execute(sim, state);
1751 measuredQubits.insert(qubits.begin(), qubits.end());
1754 const auto bits = op->AffectedBits();
1767 for (
auto bit : bits)
1768 if (measuredQubits.find(bit) != measuredQubits.end()) {
1773 for (
auto qubit : qubits)
1774 if (measuredQubits.find(qubit) != measuredQubits.end()) {
1782 op->Execute(sim, state);
1787 measuredQubits.insert(bits.begin(), bits.end());
1788 measuredQubits.insert(qubits.begin(), qubits.end());
1792 affectedQubits.insert(qubits.begin(), qubits.end());
1795 executionStopped =
true;
1796 if (executionStopped)
1797 executedOps.emplace_back(executed);
1818 const std::vector<bool> &executedOps)
const {
1826 const size_t dif = operations.size() - executedOps.size();
1828 for (
size_t i = dif; i < operations.size(); ++i)
1829 if (!executedOps[i - dif])
1830 operations[i]->Execute(sim, state);
1837 std::shared_ptr<MeasurementOperation<Time>>
1839 bool sort =
true)
const {
1840 const size_t dif = operations.size() - executedOps.size();
1841 std::vector<std::pair<Types::qubit_t, size_t>> measurements;
1842 measurements.reserve(dif);
1844 for (
size_t i = dif; i < operations.size(); ++i)
1845 if (!executedOps[i - dif] &&
1848 std::static_pointer_cast<MeasurementOperation<Time>>(operations[i]);
1849 const auto &qubits = measOp->GetQubits();
1850 const auto &bits = measOp->GetBitsIndices();
1852 for (
size_t j = 0; j < qubits.size(); ++j)
1853 measurements.emplace_back(qubits[j], bits[j]);
1859 measurements.begin(), measurements.end(),
1860 [](
const auto &p1,
const auto &p2) { return p1.first < p2.first; });
1862 return std::make_shared<MeasurementOperation<Time>>(measurements);
1874 for (
const auto &op : operations)
1897 for (
const auto &op : operations) {
1898 const auto qubits = op->AffectedQubits();
1899 if (qubits.size() <= 1)
1902 if (qubits.size() == 2) {
1903 if (std::abs(qubits[0] - qubits[1]) != 1)
1909 for (
size_t i = 1; i < qubits.size(); ++i) {
1910 if (qubits[i] < minQubit)
1911 minQubit = qubits[i];
1912 else if (qubits[i] > maxQubit)
1913 maxQubit = qubits[i];
1916 if (maxQubit - minQubit >= qubits.size())
1932 std::unordered_map<Types::qubit_t, size_t> qubits;
1933 std::unordered_map<Types::qubit_t, Types::qubits_vector> lastQubits;
1935 for (
const auto &op : operations) {
1936 const auto q = op->AffectedQubits();
1942 bool allInTheLastQubits =
true;
1944 for (
const auto qubit : q) {
1945 if (lastQubits.find(qubit) == lastQubits.end()) {
1946 allInTheLastQubits =
false;
1949 const auto &lastQ = lastQubits[qubit];
1951 for (
const auto q1 : q)
1952 if (std::find(lastQ.cbegin(), lastQ.cend(), q1) == lastQ.cend()) {
1953 allInTheLastQubits =
false;
1957 if (!allInTheLastQubits)
1962 if (allInTheLastQubits)
1965 for (
const auto qubit : q) {
1966 if (qubits[qubit] > 1)
1971 lastQubits[qubit] = q;
1988 for (
const auto &op : operations)
1989 if (!op->IsClifford())
2005 size_t cliffordOps = 0;
2006 for (
const auto &op : operations)
2007 if (op->IsClifford())
2010 return static_cast<double>(cliffordOps) / operations.size();
2023 std::unordered_set<Types::qubit_t> cliffordQubits;
2024 std::unordered_set<Types::qubit_t> nonCliffordQubits;
2026 for (
const auto &op : operations) {
2027 const auto qubits = op->AffectedQubits();
2028 if (op->IsClifford()) {
2029 for (
const auto q : qubits)
2030 cliffordQubits.insert(q);
2032 for (
const auto q : qubits)
2033 nonCliffordQubits.insert(q);
2037 for (
const auto q : nonCliffordQubits)
2038 cliffordQubits.erase(q);
2040 return cliffordQubits;
2053 std::vector<std::shared_ptr<Circuit<Time>>> circuits;
2057 std::unordered_map<Types::qubit_t, std::unordered_set<Types::qubit_t>>
2060 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitCircuitMap;
2065 for (
auto qubit : allQubits) {
2066 circuitsMap[qubit] = std::unordered_set<Types::qubit_t>{qubit};
2067 qubitCircuitMap[qubit] = qubit;
2072 for (
const auto &op : operations) {
2073 const auto qubits = op->AffectedQubits();
2078 auto qubitIt = qubits.cbegin();
2079 auto firstQubit = *qubitIt;
2081 auto firstQubitCircuit = qubitCircuitMap[firstQubit];
2085 for (; qubitIt != qubits.cend(); ++qubitIt) {
2086 auto qubit = *qubitIt;
2089 auto qubitCircuit = qubitCircuitMap[qubit];
2093 if (firstQubitCircuit != qubitCircuit) {
2095 circuitsMap[firstQubitCircuit].insert(
2096 circuitsMap[qubitCircuit].
begin(),
2097 circuitsMap[qubitCircuit].
end());
2100 for (
auto q : circuitsMap[qubitCircuit])
2101 qubitCircuitMap[q] = firstQubitCircuit;
2104 circuitsMap.erase(qubitCircuit);
2109 size_t circSize = 1ULL;
2110 circSize = std::max(circSize, circuitsMap.size());
2111 circuits.resize(circSize);
2113 for (
size_t i = 0; i < circuits.size(); ++i)
2116 std::unordered_map<Types::qubit_t, size_t> qubitsSetsToCircuit;
2118 size_t circuitNo = 0;
2119 for (
const auto &[
id, qubitSet] : circuitsMap) {
2120 qubitsSetsToCircuit[id] = circuitNo;
2127 for (
const auto &op : operations) {
2128 const auto qubits = op->AffectedQubits();
2130 if (qubits.empty()) {
2131 circuits[0]->AddOperation(op->Clone());
2135 const auto circ = qubitsSetsToCircuit[qubitCircuitMap[*qubits.cbegin()]];
2137 circuits[circ]->AddOperation(op->Clone());
2198 return operations.crbegin();
2215 auto size()
const {
return operations.size(); }
2223 auto empty()
const {
return operations.empty(); }
2241 const auto &
operator[](
size_t pos)
const {
return operations[pos]; }
2251 if (
size < operations.size())
2252 operations.resize(
size);
2266 void ReplaceThreeQubitAndSwapGates(
bool onlyThreeQubits =
false) {
2275 std::vector<std::shared_ptr<IOperation<Time>>> newops;
2276 newops.reserve(operations.size());
2280 std::shared_ptr<IQuantumGate<Time>> gate =
2281 std::static_pointer_cast<IQuantumGate<Time>>(op);
2283 if (NeedsConversion(gate, onlyThreeQubits)) {
2284 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2285 ConvertGate(gate, onlyThreeQubits);
2286 newops.insert(newops.end(), newgates.begin(), newgates.end());
2288 newops.push_back(op);
2290 std::shared_ptr<ConditionalGate<Time>> condgate =
2291 std::static_pointer_cast<ConditionalGate<Time>>(op);
2292 std::shared_ptr<IQuantumGate<Time>> gate =
2293 std::static_pointer_cast<IQuantumGate<Time>>(
2294 condgate->GetOperation());
2296 if (NeedsConversion(gate, onlyThreeQubits)) {
2297 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2298 ConvertGate(gate, onlyThreeQubits);
2299 std::shared_ptr<ICondition> cond = condgate->GetCondition();
2301 for (
auto gate : newgates)
2303 std::make_shared<ConditionalGate<Time>>(gate, cond));
2305 newops.push_back(op);
2307 newops.push_back(op);
2310 operations.swap(newops);
2322 static bool NeedsConversion(
const std::shared_ptr<IQuantumGate<Time>> &gate,
2323 bool onlyThreeQubits =
false) {
2324 const bool hasThreeQubits = gate->GetNumQubits() == 3;
2325 if (onlyThreeQubits)
2326 return hasThreeQubits;
2328 return hasThreeQubits ||
2344 static std::vector<std::shared_ptr<IGateOperation<Time>>>
2345 ConvertGate(std::shared_ptr<IQuantumGate<Time>> &gate,
2346 bool onlyThreeQubits =
false) {
2349 std::vector<std::shared_ptr<IGateOperation<Time>>> newops;
2351 if (gate->GetNumQubits() == 3) {
2354 const size_t q1 = gate->GetQubit(0);
2355 const size_t q2 = gate->GetQubit(1);
2356 const size_t q3 = gate->GetQubit(2);
2359 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2360 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2361 newops.push_back(std::make_shared<CSxDagGate<Time>>(q2, q3));
2362 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2363 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2365 const size_t q1 = gate->GetQubit(0);
2366 const size_t q2 = gate->GetQubit(1);
2367 const size_t q3 = gate->GetQubit(2);
2371 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2373 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2374 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2375 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2377 newops.push_back(std::make_shared<PhaseGate<Time>>(q2, -M_PI_2));
2379 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2380 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2381 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2383 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2385 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2387 newops.push_back(gate);
2388 }
else if (!onlyThreeQubits &&
2391 const size_t q1 = gate->GetQubit(0);
2392 const size_t q2 = gate->GetQubit(1);
2396 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2397 newops.push_back(std::make_shared<CXGate<Time>>(q2, q1));
2398 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2400 newops.push_back(gate);
2421template <
typename Time = Types::time_type>
2429 std::vector<OperationPtr>;
2489 if (approximateParamsCheck) {
2490 const auto params1 = std::static_pointer_cast<IQuantumGate<Time>>(
2493 const auto params2 = std::static_pointer_cast<IQuantumGate<Time>>(
2496 if (params1.size() != params2.size())
2499 for (
size_t j = 0; j < params1.size(); ++j)
2500 if (std::abs(params1[j] - params2[j]) > paramsEpsilon)
2526 const auto leftCondition =
2527 std::static_pointer_cast<IConditionalOperation<Time>>(
2530 const auto rightCondition =
2531 std::static_pointer_cast<IConditionalOperation<Time>>(
2534 if (leftCondition->GetBitsIndices() != rightCondition->GetBitsIndices())
2537 const auto leftEqCondition =
2538 std::static_pointer_cast<EqualCondition>(leftCondition);
2539 const auto rightEqCondition =
2540 std::static_pointer_cast<EqualCondition>(rightCondition);
2541 if (!leftEqCondition || !rightEqCondition)
2544 if (leftEqCondition->GetAllBits() != rightEqCondition->GetAllBits())
2549 std::static_pointer_cast<IConditionalOperation<Time>>(
2552 const auto rightOp =
2553 std::static_pointer_cast<IConditionalOperation<Time>>(
2563 if (leftCircuit != rightCircuit)
2634 bool approximateParamsCheck =
2636 double paramsEpsilon = 1e-8;
The controlled x rotation gate.
The controlled y rotation gate.
The controlled z rotation gate.
iterator begin() noexcept
Get the begin iterator for the operations.
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
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::set< size_t > GetQubits() const
Returns the qubits affected by the operations.
void AddResetsAtBeginningIfNeeded(Time delay=0)
Add resets at the beginning of the circuit.
void Clear()
Clears the operations from the circuit.
typename OperationsVector::value_type value_type
std::shared_ptr< Operation > OperationPtr
The shared pointer to the operation type.
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.
std::unordered_map< Types::qubit_t, Types::qubit_t > BitMapping
The (qu)bit mapping for remapping.
typename OperationsVector::difference_type difference_type
std::pair< std::vector< size_t >, std::vector< Time > > GetDepth() const
Get circuit depth.
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.
Circuit(const OperationsVector &ops={})
Construct a new Circuit object.
typename OperationsVector::pointer pointer
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.
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.
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
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.
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.
size_t GetMaxQubitIndex() const
Returns the max qubit id for all operations.
std::vector< OperationPtr > OperationsVector
The vector of 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.
typename OperationsVector::const_reverse_iterator const_reverse_iterator
const auto & operator[](size_t pos) const
Get the operation at a given position.
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.
std::vector< bool > ExecuteNonMeasurements(const std::shared_ptr< Simulators::ISimulator > &sim, OperationState &state) const
Execute the non-measurements operations from the circuit on the given simulator.
OperationType GetType() const override
Get the type of the circuit.
void ExecuteMeasurements(const std::shared_ptr< Simulators::ISimulator > &sim, OperationState &state, const std::vector< bool > &executedOps) const
Execute the measurement operations from the circuit on the given simulator.
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::unordered_map< std::vector< bool >, size_t > ExecuteResults
The results of the execution of the 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.
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.
std::vector< OperationPtr > OperationsVector
The vector of operations.
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.
Measurement operation class.
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.
const std::vector< bool > & GetResetTargets() const
Get the values to reset the qubits to.
OperationType
The type of operations.
@ kConditionalGate
conditional gate, similar with gate, but conditioned on something from 'OperationState'
@ 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 ...
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
uint_fast64_t qubit_t
The type of a qubit.