21#define _USE_MATH_DEFINES
44template <
typename Time = Types::time_type>
48 std::unordered_map<std::vector<bool>,
52 std::unordered_map<Types::qubit_t,
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,
100 for (
const auto &op : operations) op->Execute(sim, state);
131 if (index >= operations.size())
return;
132 operations[index] = op;
152 operations.insert(operations.end(), ops.begin(), ops.end());
179 void Clear() { operations.clear(); }
190 for (
auto &op : operations) newops.emplace_back(op->Clone());
192 return std::make_shared<Circuit<Time>>(newops);
205 for (
auto &op : operations) newops.push_back(op);
207 return std::make_shared<Circuit<Time>>(newops);
221 const BitMapping &bitsMap = {})
const override {
224 for (
const auto &op : operations)
225 newops.emplace_back(op->
Remap(qubitsMap, bitsMap));
227 return std::make_shared<Circuit<Time>>(newops);
244 size_t &nrCbits)
const {
252 for (
const auto &op : operations) {
253 const auto affectedBits = op->AffectedBits();
254 const auto affectedQubits = op->AffectedQubits();
256 for (
const auto qubit : affectedQubits) {
257 const auto it = newQubitsMap.find(qubit);
258 if (it == newQubitsMap.end()) {
259 newQubitsMap[qubit] = nrQubits;
264 for (
const auto bit : affectedBits) {
265 const auto it = newBitsMap.find(bit);
266 if (it == newBitsMap.end()) {
267 newBitsMap[bit] = nrCbits;
268 reverseBitsMap[nrCbits] = bit;
273 newops.emplace_back(op->Remap(newQubitsMap, newBitsMap));
276 return std::make_shared<Circuit<Time>>(newops);
295 bool ignoreNotMapped =
false,
299 if (!ignoreNotMapped && sz == 0) {
300 for (
const auto &[from, to] : bitsMap)
301 if (to > sz) sz = to;
306 for (
const auto &res : results) {
309 mappedState.Remap(bitsMap, ignoreNotMapped,
310 ignoreNotMapped ? bitsMap.size() : sz);
311 newResults[mappedState.GetAllBits()] += res.second;
327 for (
const auto &res : newResults) results[res.first] += res.second;
347 bool ignoreNotMapped =
true,
349 if (!ignoreNotMapped && sz == 0) {
350 for (
const auto &[from, to] : bitsMap)
351 if (to > sz) sz = to;
356 for (
const auto &res : newResults) {
359 mappedState.Remap(bitsMap, ignoreNotMapped,
360 ignoreNotMapped ? bitsMap.size() : sz);
361 results[mappedState.GetAllBits()] += res.second;
390 ReplaceThreeQubitAndSwapGates();
426 for (
size_t i = 0; i < operations.size(); ++i) {
427 const auto op = operations[i];
429 newops.emplace_back(op);
435 std::unordered_set<size_t> bits;
436 std::unordered_map<size_t, Types::qubit_t> measQubits;
437 std::unordered_map<size_t, Time> measDelays;
439 auto affectedBits = op->AffectedBits();
440 auto affectedQubits = op->AffectedQubits();
442 for (
size_t q = 0; q < affectedQubits.size(); ++q) {
443 bits.insert(affectedBits[q]);
444 measQubits[affectedBits[q]] = affectedQubits[q];
445 measDelays[affectedBits[q]] = op->GetDelay();
449 for (; j < operations.size(); ++j) {
450 const auto op2 = operations[j];
454 affectedQubits = op2->AffectedQubits();
457 std::static_pointer_cast<MeasurementOperation<Time>>(op2);
458 affectedBits = meas->GetBitsIndices();
459 for (
size_t q = 0; q < affectedBits.size(); ++q) {
460 bits.insert(affectedBits[q]);
461 measQubits[affectedBits[q]] = affectedQubits[q];
462 measDelays[affectedBits[q]] = op2->GetDelay();
470 for (; j < operations.size(); ++j) {
471 const auto op2 = operations[j];
476 std::static_pointer_cast<IConditionalOperation<Time>>(op2);
477 const auto condbits = condop->AffectedBits();
478 for (
const auto bit : condbits)
479 if (bits.find(bit) != bits.end()) {
481 std::vector{std::make_pair(measQubits[bit], bit)},
486 if (bits.empty())
break;
490 for (
auto bit : bits)
492 std::vector{std::make_pair(measQubits[bit], bit)},
496 operations.swap(newops);
508 for (
const auto &op : operations) {
509 const auto qbits = op->AffectedQubits();
525 size_t mn = std::numeric_limits<size_t>::max();
526 for (
const auto &op : operations) {
527 const auto qbits = op->AffectedQubits();
544 for (
const auto &op : operations) {
545 const auto cbits = op->AffectedBits();
561 size_t mn = std::numeric_limits<size_t>::max();
562 for (
const auto &op : operations) {
563 const auto cbits = op->AffectedBits();
581 std::set<size_t> qubits;
582 for (
const auto &op : operations) {
583 const auto qbits = op->AffectedQubits();
584 qubits.insert(qbits.begin(), qbits.end());
598 std::set<size_t> cbits;
599 for (
const auto &op : operations) {
600 const auto bits = op->AffectedBits();
601 cbits.insert(bits.begin(), bits.end());
616 Types::qubits_vector qubitsVec;
617 qubitsVec.reserve(qubits.size());
619 for (
auto q : qubits) qubitsVec.emplace_back(q);
633 std::vector<size_t> bitsVec;
634 bitsVec.reserve(bits.size());
636 for (
auto b : bits) bitsVec.emplace_back(b);
651 for (
const auto &op : operations)
652 if (op->NeedsEntanglementForDistribution())
return true;
665 for (
const auto &op : operations)
666 if (op->CanAffectQuantumState())
return true;
679 std::unordered_map<size_t, OperationPtr> lastOps;
681 for (
const auto &op : operations) {
682 const auto qbits = op->AffectedQubits();
683 for (
auto q : qbits) lastOps[q] = op;
697 std::unordered_map<size_t, OperationPtr> firstOps;
699 for (
const auto &op : operations) {
700 const auto qbits = op->AffectedQubits();
701 for (
auto q : qbits) {
702 if (firstOps.find(q) == firstOps.end()) firstOps[q] = op;
720 for (
const auto &[q, op] : GetLastOps)
724 operations.emplace_back(
725 std::make_shared<
Reset<Time>>(Types::qubits_vector{q}, delay));
739 for (
const auto &[q, op] : GetFirstOps)
745 std::make_shared<Reset<Time>>(Types::qubits_vector{q}, delay));
815 std::vector<std::shared_ptr<IOperation<Time>>> newops;
816 newops.reserve(operations.size());
818 for (
int i = 0; i < static_cast<int>(operations.size()); ++i) {
819 const std::shared_ptr<IOperation<Time>> &op = operations[i];
821 const auto type = op->GetType();
825 std::shared_ptr<IQuantumGate<Time>> gate =
826 std::static_pointer_cast<IQuantumGate<Time>>(op);
827 const auto qubits = gate->AffectedQubits();
829 if (qubits.size() == 1) {
832 auto gateType = gate->GetGateType();
833 bool replace =
false;
846 if (!optimizeRotationGates) {
847 newops.push_back(op);
892 for (
size_t j = i + 1; j < operations.size(); ++j) {
893 auto &nextOp = operations[j];
894 if (!nextOp->CanAffectQuantumState())
continue;
896 const auto nextQubits = nextOp->AffectedQubits();
897 bool hasQubit =
false;
899 for (
auto q : nextQubits)
900 if (q == qubits[0]) {
908 else if (nextQubits.size() != 1)
912 const auto nextType = nextOp->GetType();
916 const auto &nextGate =
917 std::static_pointer_cast<SingleQubitGate<Time>>(nextOp);
918 if (nextGate->GetGateType() == gateType) {
920 const auto params1 = gate->GetParams();
921 const auto params2 = nextGate->GetParams();
923 const double param = params1[0] + params2[0];
925 gate->GetDelay() + nextGate->GetDelay();
929 qubits[0], param, delay));
932 qubits[0], param, delay));
935 qubits[0], param, delay));
938 qubits[0], param, delay));
940 nextOp = std::make_shared<NoOperation<Time>>();
945 nextGate->GetGateType() ==
948 nextGate->GetGateType() ==
953 const auto delay = gate->GetDelay() + nextGate->GetDelay();
956 nextOp = std::make_shared<NoOperation<Time>>();
961 nextGate->GetGateType() ==
964 nextGate->GetGateType() ==
969 const auto delay = gate->GetDelay() + nextGate->GetDelay();
972 nextOp = std::make_shared<NoOperation<Time>>();
977 nextGate->GetGateType() ==
981 const auto delay = gate->GetDelay() + nextGate->GetDelay();
984 nextOp = std::make_shared<NoOperation<Time>>();
989 nextGate->GetGateType() ==
993 const auto delay = gate->GetDelay() + nextGate->GetDelay();
996 nextOp = std::make_shared<NoOperation<Time>>();
1001 (nextGate->GetGateType() ==
1003 nextGate->GetGateType() ==
1005 nextGate->GetGateType() ==
1007 nextGate->GetGateType() ==
1009 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1012 param2 = 0.5 * M_PI;
1013 else if (nextGate->GetGateType() ==
1015 param2 = -0.5 * M_PI;
1016 else if (nextGate->GetGateType() ==
1018 param2 = 0.25 * M_PI;
1020 param2 = -0.25 * M_PI;
1022 const auto param = gate->GetParams()[0] + param2;
1024 qubits[0], param, delay));
1025 nextOp = std::make_shared<NoOperation<Time>>();
1029 }
else if (nextGate->GetGateType() ==
1035 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1038 param1 = -0.5 * M_PI;
1040 param1 = 0.5 * M_PI;
1042 param1 = -0.25 * M_PI;
1044 param1 = 0.25 * M_PI;
1046 const auto param = nextGate->GetParams()[0] + param1;
1048 qubits[0], param, delay));
1049 nextOp = std::make_shared<NoOperation<Time>>();
1058 if (!found) newops.push_back(op);
1062 newops.push_back(op);
1065 }
else if (qubits.size() == 2) {
1066 auto gateType = gate->GetGateType();
1067 bool replace =
false;
1080 if (!optimizeRotationGates) {
1081 newops.push_back(op);
1112 for (
size_t j = i + 1; j < operations.size(); ++j) {
1113 auto &nextOp = operations[j];
1114 if (!nextOp->CanAffectQuantumState())
continue;
1116 const auto nextQubits = nextOp->AffectedQubits();
1118 bool hasQubit =
false;
1120 for (
auto q : nextQubits)
1121 if (q == qubits[0] || q == qubits[1]) {
1129 else if (nextQubits.size() != 2)
1134 !((qubits[0] == nextQubits[0] &&
1135 qubits[1] == nextQubits[1]) ||
1136 (qubits[0] == nextQubits[1] &&
1137 qubits[1] == nextQubits[0])))
1139 else if (!(qubits[0] == nextQubits[0] &&
1140 qubits[1] == nextQubits[1]))
1143 const auto nextType = nextOp->GetType();
1147 const auto &nextGate =
1148 std::static_pointer_cast<TwoQubitsGate<Time>>(nextOp);
1149 if (nextGate->GetGateType() == gateType) {
1151 const auto params1 = gate->GetParams();
1152 const auto params2 = nextGate->GetParams();
1153 const double param = params1[0] + params2[0];
1155 gate->GetDelay() + nextGate->GetDelay();
1159 qubits[0], qubits[1], param, delay));
1162 qubits[0], qubits[1], param, delay));
1165 qubits[0], qubits[1], param, delay));
1168 qubits[0], qubits[1], param, delay));
1170 nextOp = std::make_shared<NoOperation<Time>>();
1181 if (!found) newops.push_back(op);
1185 newops.push_back(op);
1188 }
else if (qubits.size() == 3) {
1189 auto gateType = gate->GetGateType();
1202 for (
size_t j = i + 1; j < operations.size(); ++j) {
1203 auto &nextOp = operations[j];
1204 if (!nextOp->CanAffectQuantumState())
continue;
1206 const auto nextQubits = nextOp->AffectedQubits();
1208 bool hasQubit =
false;
1210 for (
auto q : nextQubits)
1211 if (q == qubits[0] || q == qubits[1] || q == qubits[2]) {
1219 else if (nextQubits.size() != 3)
1224 (qubits[0] != nextQubits[0] ||
1225 !((qubits[1] == nextQubits[1] &&
1226 qubits[2] == nextQubits[2]) ||
1227 (qubits[1] == nextQubits[2] &&
1228 qubits[2] == nextQubits[1]))))
1231 (qubits[2] != nextQubits[2] ||
1232 !(qubits[1] == nextQubits[1] &&
1233 qubits[2] == nextQubits[2]) ||
1234 !(qubits[1] == nextQubits[2] &&
1235 qubits[2] == nextQubits[1])))
1238 const auto nextType = nextOp->GetType();
1242 const auto &nextGate =
1243 std::static_pointer_cast<ThreeQubitsGate<Time>>(nextOp);
1244 if (nextGate->GetGateType() == gateType) {
1245 nextOp = std::make_shared<NoOperation<Time>>();
1254 if (!found) newops.push_back(op);
1258 newops.push_back(op);
1262 newops.push_back(op);
1264 newops.push_back(op);
1267 operations.swap(newops);
1279 newops.reserve(operations.size());
1283 std::unordered_map<Types::qubit_t, std::vector<OperationPtr>> qubitOps;
1285 std::vector<OperationPtr> lastOps(qubitsNo);
1287 std::unordered_map<OperationPtr, std::unordered_set<OperationPtr>>
1290 for (
const auto &op : operations) {
1291 std::unordered_set<OperationPtr> dependencies;
1293 const auto cbits = op->AffectedBits();
1294 for (
auto c : cbits) {
1295 const auto lastOp = lastOps[c];
1296 if (lastOp) dependencies.insert(lastOp);
1299 const auto qubits = op->AffectedQubits();
1300 for (
auto q : qubits) {
1301 qubitOps[q].push_back(op);
1303 const auto lastOp = lastOps[q];
1304 if (lastOp) dependencies.insert(lastOp);
1309 for (
auto c : cbits) lastOps[c] = op;
1311 dependenciesMap[op] = dependencies;
1315 std::vector<Types::qubit_t> indices(qubitsNo, 0);
1317 while (!dependenciesMap.empty()) {
1322 for (
size_t q = 0; q < qubitsNo; ++q) {
1323 if (qubitOps.find(q) ==
1328 const auto &ops = qubitOps[q];
1329 const auto &op = ops[indices[q]];
1334 bool hasDependencies =
false;
1336 for (
const auto &opd : dependenciesMap[op])
1337 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1338 hasDependencies =
true;
1342 if (!hasDependencies) {
1350 dependenciesMap.erase(nextOp);
1352 const auto qubits = nextOp->AffectedQubits();
1353 for (
auto q : qubits) {
1355 if (indices[q] >= qubitOps[q].
size()) qubitOps.erase(q);
1358 newops.emplace_back(std::move(nextOp));
1363 for (Types::qubit_t q = 0; q < qubitsNo; ++q) {
1364 if (qubitOps.find(q) ==
1369 const auto &ops = qubitOps[q];
1370 const auto &op = ops[indices[q]];
1372 bool hasDependencies =
false;
1374 for (
const auto &opd : dependenciesMap[op])
1375 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1376 hasDependencies =
true;
1380 if (!hasDependencies) {
1387 dependenciesMap.erase(nextOp);
1389 const auto qubits = nextOp->AffectedQubits();
1390 for (
auto q : qubits) {
1392 if (indices[q] >= qubitOps[q].
size()) qubitOps.erase(q);
1395 newops.emplace_back(std::move(nextOp));
1399 assert(newops.size() == operations.size());
1401 operations.swap(newops);
1413 std::pair<std::vector<size_t>, std::vector<Time>>
GetDepth()
const {
1418 std::vector<Time> qubitTimes(qubitsNo, 0);
1419 std::vector<size_t> qubitDepths(qubitsNo, 0);
1421 std::unordered_map<size_t, size_t> fromQubits;
1423 for (
const auto &op : operations) {
1424 const auto qbits = op->AffectedQubits();
1425 const auto delay = op->GetDelay();
1429 for (
auto q : qbits) {
1430 qubitTimes[q] += delay;
1432 if (qubitTimes[q] > maxTime) maxTime = qubitTimes[q];
1433 if (qubitDepths[q] > maxDepth) maxDepth = qubitDepths[q];
1436 const auto t = op->GetType();
1437 std::vector<size_t> condbits;
1445 condbits = op->AffectedBits();
1447 for (
auto bit : condbits) {
1448 if (fromQubits.find(bit) != fromQubits.end()) bit = fromQubits[bit];
1451 for (
auto q : qbits) {
1457 if (found || bit >= qubitsNo)
continue;
1459 qubitTimes[bit] += delay;
1461 if (qubitTimes[bit] > maxTime) maxTime = qubitTimes[bit];
1462 if (qubitDepths[bit] > maxDepth) maxDepth = qubitDepths[bit];
1466 const auto condMeas =
1467 std::static_pointer_cast<ConditionalMeasurement<Time>>(op);
1468 const auto meas = condMeas->GetOperation();
1469 const auto measQubits = meas->AffectedQubits();
1470 const auto measBits = meas->AffectedBits();
1472 for (
size_t i = 0; i < measQubits.size(); ++i) {
1473 if (i < measBits.size())
1474 fromQubits[measBits[i]] = measQubits[i];
1476 fromQubits[measQubits[i]] = measQubits[i];
1480 condbits = op->AffectedBits();
1482 for (
size_t i = 0; i < qbits.size(); ++i) {
1483 if (i < condbits.size())
1484 fromQubits[condbits[i]] = qbits[i];
1486 fromQubits[qbits[i]] = qbits[i];
1490 for (
auto q : qbits) {
1491 qubitTimes[q] = maxTime;
1492 qubitDepths[q] = maxDepth;
1495 for (
auto bit : condbits) {
1496 qubitTimes[bit] = maxTime;
1497 qubitDepths[bit] = maxDepth;
1501 return std::make_pair(qubitDepths, qubitTimes);
1514 auto [qubitDepths, qubitTimes] =
GetDepth();
1517 size_t maxDepth = 0;
1518 for (
size_t qubit = 0; qubit < qubitDepths.size(); ++qubit) {
1519 if (qubitTimes[qubit] > maxTime) maxTime = qubitTimes[qubit];
1520 if (qubitDepths[qubit] > maxDepth) maxDepth = qubitDepths[qubit];
1523 return std::make_pair(maxDepth, maxTime);
1543 if (pos >= operations.size())
return nullptr;
1545 return operations[pos];
1562 Types::qubit_t endQubit)
const {
1564 newops.reserve(operations.size());
1566 for (
const auto &op : operations) {
1567 const auto qubits = op->AffectedQubits();
1568 bool containsOutsideQubits =
false;
1569 bool containsInsideQubits =
false;
1570 for (
const auto q : qubits) {
1571 if (q < startQubit || q > endQubit) {
1572 containsOutsideQubits =
true;
1573 if (containsInsideQubits)
break;
1575 containsInsideQubits =
true;
1576 if (containsOutsideQubits)
break;
1580 if (containsInsideQubits) {
1581 if (containsOutsideQubits)
1582 throw std::runtime_error(
1583 "Cannot cut the circuit with the specified interval");
1584 newops.emplace_back(op->Clone());
1588 return std::make_shared<Circuit<Time>>(newops);
1602 std::unordered_set<Types::qubit_t> measuredQubits;
1603 std::unordered_set<Types::qubit_t> affectedQubits;
1604 std::unordered_set<Types::qubit_t> resetQubits;
1606 for (
const auto &op : operations) {
1607 const auto qubits = op->AffectedQubits();
1610 for (
const auto qbit : qubits)
1611 if (resetQubits.find(qbit) !=
1625 measuredQubits.insert(qubits.begin(), qubits.end());
1633 for (
const auto qbit : qubits) {
1637 if (affectedQubits.find(qbit) != affectedQubits.end() ||
1638 measuredQubits.find(qbit) != measuredQubits.end())
1639 resetQubits.insert(qbit);
1641 affectedQubits.insert(qbit);
1644 for (
const auto qbit : qubits) {
1645 if (measuredQubits.find(qbit) !=
1650 if (resetQubits.find(qbit) !=
1655 affectedQubits.insert(qbit);
1676 const std::shared_ptr<Simulators::ISimulator> &sim,
1678 std::vector<bool> executedOps;
1679 executedOps.reserve(operations.size());
1681 std::unordered_set<Types::qubit_t> measuredQubits;
1682 std::unordered_set<Types::qubit_t> affectedQubits;
1684 bool executionStopped =
false;
1686 for (
size_t i = 0; i < operations.size(); ++i) {
1687 auto &op = operations[i];
1688 const auto qubits = op->AffectedQubits();
1690 bool executed =
false;
1696 measuredQubits.insert(qubits.begin(), qubits.end());
1701 for (
auto qubit : qubits)
1702 if (affectedQubits.find(qubit) != affectedQubits.end()) {
1708 if (sim) op->Execute(sim, state);
1710 measuredQubits.insert(qubits.begin(), qubits.end());
1713 const auto bits = op->AffectedBits();
1726 for (
auto bit : bits)
1727 if (measuredQubits.find(bit) != measuredQubits.end()) {
1732 for (
auto qubit : qubits)
1733 if (measuredQubits.find(qubit) != measuredQubits.end()) {
1740 if (sim) op->Execute(sim, state);
1745 measuredQubits.insert(bits.begin(), bits.end());
1746 measuredQubits.insert(qubits.begin(), qubits.end());
1750 affectedQubits.insert(qubits.begin(), qubits.end());
1752 if (!executed) executionStopped =
true;
1753 if (executionStopped) executedOps.emplace_back(executed);
1774 const std::vector<bool> &executedOps)
const {
1781 const size_t dif = operations.size() - executedOps.size();
1783 for (
size_t i = dif; i < operations.size(); ++i)
1784 if (!executedOps[i - dif]) operations[i]->Execute(sim, state);
1792 const std::vector<bool> &executedOps,
bool sort =
true)
const {
1793 const size_t dif = operations.size() - executedOps.size();
1794 std::vector<std::pair<Types::qubit_t, size_t>> measurements;
1795 measurements.reserve(dif);
1797 for (
size_t i = dif; i < operations.size(); ++i)
1798 if (!executedOps[i - dif] &&
1801 std::static_pointer_cast<MeasurementOperation<Time>>(operations[i]);
1802 const auto &qubits = measOp->GetQubits();
1803 const auto &bits = measOp->GetBitsIndices();
1805 for (
size_t j = 0; j < qubits.size(); ++j)
1806 measurements.emplace_back(qubits[j], bits[j]);
1812 measurements.begin(), measurements.end(),
1813 [](
const auto &p1,
const auto &p2) { return p1.first < p2.first; });
1815 return std::make_shared<MeasurementOperation<Time>>(measurements);
1827 for (
const auto &op : operations)
1850 for (
const auto &op : operations) {
1851 const auto qubits = op->AffectedQubits();
1852 if (qubits.size() <= 1)
continue;
1854 if (qubits.size() == 2) {
1855 if (std::abs(qubits[0] - qubits[1]) != 1)
return false;
1857 Types::qubit_t minQubit = qubits[0];
1858 Types::qubit_t maxQubit = qubits[0];
1860 for (
size_t i = 1; i < qubits.size(); ++i) {
1861 if (qubits[i] < minQubit)
1862 minQubit = qubits[i];
1863 else if (qubits[i] > maxQubit)
1864 maxQubit = qubits[i];
1867 if (maxQubit - minQubit >= qubits.size())
return false;
1882 std::unordered_map<Types::qubit_t, size_t> qubits;
1883 std::unordered_map<Types::qubit_t, Types::qubits_vector> lastQubits;
1885 for (
const auto &op : operations) {
1886 const auto q = op->AffectedQubits();
1889 if (q.size() <= 1)
continue;
1891 bool allInTheLastQubits =
true;
1893 for (
const auto qubit : q) {
1894 if (lastQubits.find(qubit) == lastQubits.end()) {
1895 allInTheLastQubits =
false;
1898 const auto &lastQ = lastQubits[qubit];
1900 for (
const auto q1 : q)
1901 if (std::find(lastQ.cbegin(), lastQ.cend(), q1) == lastQ.cend()) {
1902 allInTheLastQubits =
false;
1906 if (!allInTheLastQubits)
break;
1910 if (allInTheLastQubits)
continue;
1912 for (
const auto qubit : q) {
1913 if (qubits[qubit] > 1)
1918 lastQubits[qubit] = q;
1935 for (
const auto &op : operations)
1936 if (!op->IsClifford())
return false;
1951 size_t cliffordOps = 0;
1952 for (
const auto &op : operations)
1953 if (op->IsClifford()) ++cliffordOps;
1955 return static_cast<double>(cliffordOps) / operations.size();
1968 std::unordered_set<Types::qubit_t> cliffordQubits;
1969 std::unordered_set<Types::qubit_t> nonCliffordQubits;
1971 for (
const auto &op : operations) {
1972 const auto qubits = op->AffectedQubits();
1973 if (op->IsClifford()) {
1974 for (
const auto q : qubits) cliffordQubits.insert(q);
1976 for (
const auto q : qubits) nonCliffordQubits.insert(q);
1980 for (
const auto q : nonCliffordQubits) cliffordQubits.erase(q);
1982 return cliffordQubits;
1995 std::vector<std::shared_ptr<Circuit<Time>>> circuits;
1999 std::unordered_map<Types::qubit_t, std::unordered_set<Types::qubit_t>>
2002 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitCircuitMap;
2007 for (
auto qubit : allQubits) {
2008 circuitsMap[qubit] = std::unordered_set<Types::qubit_t>{qubit};
2009 qubitCircuitMap[qubit] = qubit;
2014 for (
const auto &op : operations) {
2015 const auto qubits = op->AffectedQubits();
2017 if (qubits.empty())
continue;
2019 auto qubitIt = qubits.cbegin();
2020 auto firstQubit = *qubitIt;
2022 auto firstQubitCircuit = qubitCircuitMap[firstQubit];
2026 for (; qubitIt != qubits.cend(); ++qubitIt) {
2027 auto qubit = *qubitIt;
2030 auto qubitCircuit = qubitCircuitMap[qubit];
2034 if (firstQubitCircuit != qubitCircuit) {
2036 circuitsMap[firstQubitCircuit].insert(
2037 circuitsMap[qubitCircuit].
begin(),
2038 circuitsMap[qubitCircuit].end());
2041 for (
auto q : circuitsMap[qubitCircuit])
2042 qubitCircuitMap[q] = firstQubitCircuit;
2045 circuitsMap.erase(qubitCircuit);
2050 size_t circSize = 1ULL;
2051 circSize = std::max(circSize, circuitsMap.size());
2052 circuits.resize(circSize);
2054 for (
size_t i = 0; i < circuits.size(); ++i)
2057 std::unordered_map<Types::qubit_t, size_t> qubitsSetsToCircuit;
2059 size_t circuitNo = 0;
2060 for (
const auto &[
id, qubitSet] : circuitsMap) {
2061 qubitsSetsToCircuit[id] = circuitNo;
2068 for (
const auto &op : operations) {
2071 if (qubits.empty()) {
2072 circuits[0]->AddOperation(op->Clone());
2076 const auto circ = qubitsSetsToCircuit[qubitCircuitMap[*qubits.cbegin()]];
2078 circuits[circ]->AddOperation(op->Clone());
2139 return operations.crbegin();
2156 auto size()
const {
return operations.size(); }
2164 auto empty()
const {
return operations.empty(); }
2182 const auto &
operator[](
size_t pos)
const {
return operations[pos]; }
2192 if (
size < operations.size()) operations.resize(
size);
2206 void ReplaceThreeQubitAndSwapGates(
bool onlyThreeQubits =
false) {
2215 std::vector<std::shared_ptr<IOperation<Time>>> newops;
2216 newops.reserve(operations.size());
2220 std::shared_ptr<IQuantumGate<Time>> gate =
2221 std::static_pointer_cast<IQuantumGate<Time>>(op);
2223 if (NeedsConversion(gate, onlyThreeQubits)) {
2224 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2225 ConvertGate(gate, onlyThreeQubits);
2226 newops.insert(newops.end(), newgates.begin(), newgates.end());
2228 newops.push_back(op);
2230 std::shared_ptr<ConditionalGate<Time>> condgate =
2231 std::static_pointer_cast<ConditionalGate<Time>>(op);
2232 std::shared_ptr<IQuantumGate<Time>> gate =
2233 std::static_pointer_cast<IQuantumGate<Time>>(
2234 condgate->GetOperation());
2236 if (NeedsConversion(gate, onlyThreeQubits)) {
2237 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2238 ConvertGate(gate, onlyThreeQubits);
2239 std::shared_ptr<ICondition> cond = condgate->GetCondition();
2241 for (
auto gate : newgates)
2243 std::make_shared<ConditionalGate<Time>>(gate, cond));
2245 newops.push_back(op);
2247 newops.push_back(op);
2250 operations.swap(newops);
2262 static bool NeedsConversion(
const std::shared_ptr<IQuantumGate<Time>> &gate,
2263 bool onlyThreeQubits =
false) {
2264 const bool hasThreeQubits = gate->GetNumQubits() == 3;
2265 if (onlyThreeQubits)
return hasThreeQubits;
2267 return hasThreeQubits ||
2283 static std::vector<std::shared_ptr<IGateOperation<Time>>> ConvertGate(
2284 std::shared_ptr<IQuantumGate<Time>> &gate,
bool onlyThreeQubits =
false) {
2287 std::vector<std::shared_ptr<IGateOperation<Time>>> newops;
2289 if (gate->GetNumQubits() == 3) {
2292 const size_t q1 = gate->GetQubit(0);
2293 const size_t q2 = gate->GetQubit(1);
2294 const size_t q3 = gate->GetQubit(2);
2297 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2298 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2299 newops.push_back(std::make_shared<CSxDagGate<Time>>(q2, q3));
2300 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2301 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2303 const size_t q1 = gate->GetQubit(0);
2304 const size_t q2 = gate->GetQubit(1);
2305 const size_t q3 = gate->GetQubit(2);
2309 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2311 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2312 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2313 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2315 newops.push_back(std::make_shared<PhaseGate<Time>>(q2, -M_PI_2));
2317 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2318 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2319 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2321 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2323 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2325 newops.push_back(gate);
2326 }
else if (!onlyThreeQubits &&
2329 const size_t q1 = gate->GetQubit(0);
2330 const size_t q2 = gate->GetQubit(1);
2334 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2335 newops.push_back(std::make_shared<CXGate<Time>>(q2, q1));
2336 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2338 newops.push_back(gate);
2359template <
typename Time = Types::time_type>
2367 std::vector<OperationPtr>;
2421 std::static_pointer_cast<IQuantumGate<Time>>(
2427 if (approximateParamsCheck) {
2428 const auto params1 = std::static_pointer_cast<IQuantumGate<Time>>(
2431 const auto params2 = std::static_pointer_cast<IQuantumGate<Time>>(
2434 if (params1.size() != params2.size())
return false;
2436 for (
size_t j = 0; j < params1.size(); ++j)
2437 if (std::abs(params1[j] - params2[j]) > paramsEpsilon)
2463 const auto leftCondition =
2464 std::static_pointer_cast<IConditionalOperation<Time>>(
2467 const auto rightCondition =
2468 std::static_pointer_cast<IConditionalOperation<Time>>(
2471 if (leftCondition->GetBitsIndices() !=
2472 rightCondition->GetBitsIndices())
2475 const auto leftEqCondition =
2476 std::static_pointer_cast<EqualCondition>(leftCondition);
2477 const auto rightEqCondition =
2478 std::static_pointer_cast<EqualCondition>(rightCondition);
2479 if (!leftEqCondition || !rightEqCondition)
return false;
2481 if (leftEqCondition->GetAllBits() != rightEqCondition->GetAllBits())
2486 std::static_pointer_cast<IConditionalOperation<Time>>(
2489 const auto rightOp =
2490 std::static_pointer_cast<IConditionalOperation<Time>>(
2500 if (leftCircuit != rightCircuit)
return false;
2571 bool approximateParamsCheck =
2573 double paramsEpsilon = 1e-8;
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::set< size_t > GetQubits() const
Returns the qubits affected by the operations.
std::vector< OperationPtr > OperationsVector
The vector of 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.
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
std::unordered_map< std::vector< bool >, size_t > ExecuteResults
The results of the execution of the circuit.
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.
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.
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::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.
Circuit class for holding the sequence of operations that can be compared with another 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.
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.
Time GetDelay() const
Get the delay of the operation.
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 ...