22#define _USE_MATH_DEFINES
47template <
typename Time = Types::time_type>
51 std::unordered_map<std::vector<bool>,
63 std::vector<OperationPtr>;
65 using value_type =
typename OperationsVector::value_type;
67 using pointer =
typename OperationsVector::pointer;
69 using reference =
typename OperationsVector::reference;
71 using size_type =
typename OperationsVector::size_type;
74 using iterator =
typename OperationsVector::iterator;
78 typename OperationsVector::const_reverse_iterator;
98 void Execute(
const std::shared_ptr<Simulators::ISimulator>& sim,
114 void ExecuteBD(
const std::shared_ptr<Simulators::ISimulator> &sim,
119 for (
const auto& op : operations) {
120 op->Execute(sim, state);
122 const auto bondDim = sim->GetCurrentMaxBondDimension();
123 if (bondDim > *curMaxBondDim) *curMaxBondDim = bondDim;
165 if (index >= operations.size())
return;
166 operations[index] = op;
186 operations.insert(operations.end(), ops.begin(), ops.end());
213 void Clear() { operations.clear(); }
224 for (
auto &op : operations) newops.emplace_back(op->Clone());
226 return std::make_shared<Circuit<Time>>(newops);
239 for (
auto &op : operations) newops.push_back(op);
241 return std::make_shared<Circuit<Time>>(newops);
255 const BitMapping &bitsMap = {})
const override {
258 for (
const auto &op : operations)
259 newops.emplace_back(op->Remap(qubitsMap, bitsMap));
261 return std::make_shared<Circuit<Time>>(newops);
278 size_t &nrCbits)
const {
286 for (
const auto &op : operations) {
287 const auto affectedBits = op->AffectedBits();
288 const auto affectedQubits = op->AffectedQubits();
290 for (
const auto qubit : affectedQubits) {
291 const auto it = newQubitsMap.find(qubit);
292 if (it == newQubitsMap.end()) {
293 newQubitsMap[qubit] = nrQubits;
298 for (
const auto bit : affectedBits) {
299 const auto it = newBitsMap.find(bit);
300 if (it == newBitsMap.end()) {
301 newBitsMap[bit] = nrCbits;
302 reverseBitsMap[nrCbits] = bit;
307 newops.emplace_back(op->Remap(newQubitsMap, newBitsMap));
310 return std::make_shared<Circuit<Time>>(newops);
329 bool ignoreNotMapped =
false,
333 if (!ignoreNotMapped && sz == 0) {
334 for (
const auto &[from, to] : bitsMap)
335 if (to > sz) sz = to;
340 for (
const auto &res : results) {
343 mappedState.Remap(bitsMap, ignoreNotMapped,
344 ignoreNotMapped ? bitsMap.size() : sz);
345 newResults[mappedState.GetAllBits()] += res.second;
361 for (
const auto &res : newResults) results[res.first] += res.second;
381 bool ignoreNotMapped =
true,
383 if (!ignoreNotMapped && sz == 0) {
384 for (
const auto &[from, to] : bitsMap)
385 if (to > sz) sz = to;
390 for (
const auto &res : newResults) {
393 mappedState.Remap(bitsMap, ignoreNotMapped,
394 ignoreNotMapped ? bitsMap.size() : sz);
395 results[mappedState.GetAllBits()] += res.second;
424 ReplaceThreeQubitAndSwapGates();
460 for (
size_t i = 0; i < operations.size(); ++i) {
461 const auto op = operations[i];
463 newops.emplace_back(op);
469 std::unordered_set<size_t> bits;
470 std::unordered_map<size_t, Types::qubit_t> measQubits;
471 std::unordered_map<size_t, Time> measDelays;
473 auto affectedBits = op->AffectedBits();
474 auto affectedQubits = op->AffectedQubits();
476 for (
size_t q = 0; q < affectedQubits.size(); ++q) {
477 bits.insert(affectedBits[q]);
478 measQubits[affectedBits[q]] = affectedQubits[q];
479 measDelays[affectedBits[q]] = op->GetDelay();
483 for (; j < operations.size(); ++j) {
484 const auto op2 = operations[j];
488 affectedQubits = op2->AffectedQubits();
491 std::static_pointer_cast<MeasurementOperation<Time>>(op2);
492 affectedBits = meas->GetBitsIndices();
493 for (
size_t q = 0; q < affectedBits.size(); ++q) {
494 bits.insert(affectedBits[q]);
495 measQubits[affectedBits[q]] = affectedQubits[q];
496 measDelays[affectedBits[q]] = op2->GetDelay();
504 for (; j < operations.size(); ++j) {
505 const auto op2 = operations[j];
510 std::static_pointer_cast<IConditionalOperation<Time>>(op2);
511 const auto condbits = condop->AffectedBits();
512 for (
const auto bit : condbits)
513 if (bits.find(bit) != bits.end()) {
515 std::vector{std::make_pair(measQubits[bit], bit)},
520 if (bits.empty())
break;
524 for (
auto bit : bits)
526 std::vector{std::make_pair(measQubits[bit], bit)},
530 operations.swap(newops);
542 for (
const auto &op : operations) {
543 const auto qbits = op->AffectedQubits();
559 size_t mn = std::numeric_limits<size_t>::max();
560 for (
const auto &op : operations) {
561 const auto qbits = op->AffectedQubits();
578 for (
const auto &op : operations) {
579 const auto cbits = op->AffectedBits();
595 size_t mn = std::numeric_limits<size_t>::max();
596 for (
const auto &op : operations) {
597 const auto cbits = op->AffectedBits();
615 std::set<size_t> qubits;
616 for (
const auto &op : operations) {
617 const auto qbits = op->AffectedQubits();
618 qubits.insert(qbits.begin(), qbits.end());
632 std::set<size_t> cbits;
633 for (
const auto &op : operations) {
634 const auto bits = op->AffectedBits();
635 cbits.insert(bits.begin(), bits.end());
651 qubitsVec.reserve(qubits.size());
653 for (
auto q : qubits) qubitsVec.emplace_back(q);
667 std::vector<size_t> bitsVec;
668 bitsVec.reserve(bits.size());
670 for (
auto b : bits) bitsVec.emplace_back(b);
685 for (
const auto &op : operations)
686 if (op->NeedsEntanglementForDistribution())
return true;
699 for (
const auto &op : operations)
700 if (op->CanAffectQuantumState())
return true;
713 std::unordered_map<size_t, OperationPtr> lastOps;
715 for (
const auto &op : operations) {
716 const auto qbits = op->AffectedQubits();
717 for (
auto q : qbits) lastOps[q] = op;
731 std::unordered_map<size_t, OperationPtr> firstOps;
733 for (
const auto &op : operations) {
734 const auto qbits = op->AffectedQubits();
735 for (
auto q : qbits) {
736 if (firstOps.find(q) == firstOps.end()) firstOps[q] = op;
754 for (
const auto &[q, op] : GetLastOps)
758 operations.emplace_back(
773 for (
const auto &[q, op] : GetFirstOps)
849 std::vector<std::shared_ptr<IOperation<Time>>> newops;
850 newops.reserve(operations.size());
852 for (
int i = 0; i < static_cast<int>(operations.size()); ++i) {
853 const std::shared_ptr<IOperation<Time>> &op = operations[i];
855 const auto type = op->GetType();
859 std::shared_ptr<IQuantumGate<Time>> gate =
860 std::static_pointer_cast<IQuantumGate<Time>>(op);
861 const auto qubits = gate->AffectedQubits();
863 if (qubits.size() == 1) {
866 auto gateType = gate->GetGateType();
867 bool replace =
false;
880 if (!optimizeRotationGates) {
881 newops.push_back(op);
926 for (
size_t j = i + 1; j < operations.size(); ++j) {
927 auto &nextOp = operations[j];
928 if (!nextOp->CanAffectQuantumState())
continue;
930 const auto nextQubits = nextOp->AffectedQubits();
931 bool hasQubit =
false;
933 for (
auto q : nextQubits)
934 if (q == qubits[0]) {
942 else if (nextQubits.size() != 1)
946 const auto nextType = nextOp->GetType();
950 const auto &nextGate =
951 std::static_pointer_cast<SingleQubitGate<Time>>(nextOp);
952 if (nextGate->GetGateType() == gateType) {
954 const auto params1 = gate->GetParams();
955 const auto params2 = nextGate->GetParams();
957 const double param = params1[0] + params2[0];
959 gate->GetDelay() + nextGate->GetDelay();
963 qubits[0], param, delay));
966 qubits[0], param, delay));
969 qubits[0], param, delay));
972 qubits[0], param, delay));
974 nextOp = std::make_shared<NoOperation<Time>>();
979 nextGate->GetGateType() ==
982 nextGate->GetGateType() ==
987 const auto delay = gate->GetDelay() + nextGate->GetDelay();
990 nextOp = std::make_shared<NoOperation<Time>>();
995 nextGate->GetGateType() ==
998 nextGate->GetGateType() ==
1003 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1006 nextOp = std::make_shared<NoOperation<Time>>();
1011 nextGate->GetGateType() ==
1015 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1018 nextOp = std::make_shared<NoOperation<Time>>();
1023 nextGate->GetGateType() ==
1027 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1030 nextOp = std::make_shared<NoOperation<Time>>();
1035 (nextGate->GetGateType() ==
1037 nextGate->GetGateType() ==
1039 nextGate->GetGateType() ==
1041 nextGate->GetGateType() ==
1043 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1046 param2 = 0.5 * M_PI;
1047 else if (nextGate->GetGateType() ==
1049 param2 = -0.5 * M_PI;
1050 else if (nextGate->GetGateType() ==
1052 param2 = 0.25 * M_PI;
1054 param2 = -0.25 * M_PI;
1056 const auto param = gate->GetParams()[0] + param2;
1058 qubits[0], param, delay));
1059 nextOp = std::make_shared<NoOperation<Time>>();
1063 }
else if (nextGate->GetGateType() ==
1069 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1072 param1 = -0.5 * M_PI;
1074 param1 = 0.5 * M_PI;
1076 param1 = -0.25 * M_PI;
1078 param1 = 0.25 * M_PI;
1080 const auto param = nextGate->GetParams()[0] + param1;
1082 qubits[0], param, delay));
1083 nextOp = std::make_shared<NoOperation<Time>>();
1092 if (!found) newops.push_back(op);
1096 newops.push_back(op);
1099 }
else if (qubits.size() == 2) {
1100 auto gateType = gate->GetGateType();
1101 bool replace =
false;
1114 if (!optimizeRotationGates) {
1115 newops.push_back(op);
1146 for (
size_t j = i + 1; j < operations.size(); ++j) {
1147 auto &nextOp = operations[j];
1148 if (!nextOp->CanAffectQuantumState())
continue;
1150 const auto nextQubits = nextOp->AffectedQubits();
1152 bool hasQubit =
false;
1154 for (
auto q : nextQubits)
1155 if (q == qubits[0] || q == qubits[1]) {
1163 else if (nextQubits.size() != 2)
1168 !((qubits[0] == nextQubits[0] &&
1169 qubits[1] == nextQubits[1]) ||
1170 (qubits[0] == nextQubits[1] &&
1171 qubits[1] == nextQubits[0])))
1173 else if (!(qubits[0] == nextQubits[0] &&
1174 qubits[1] == nextQubits[1]))
1177 const auto nextType = nextOp->GetType();
1181 const auto &nextGate =
1182 std::static_pointer_cast<TwoQubitsGate<Time>>(nextOp);
1183 if (nextGate->GetGateType() == gateType) {
1185 const auto params1 = gate->GetParams();
1186 const auto params2 = nextGate->GetParams();
1187 const double param = params1[0] + params2[0];
1189 gate->GetDelay() + nextGate->GetDelay();
1193 qubits[0], qubits[1], param, delay));
1196 qubits[0], qubits[1], param, delay));
1199 qubits[0], qubits[1], param, delay));
1202 qubits[0], qubits[1], param, delay));
1204 nextOp = std::make_shared<NoOperation<Time>>();
1215 if (!found) newops.push_back(op);
1219 newops.push_back(op);
1222 }
else if (qubits.size() == 3) {
1223 auto gateType = gate->GetGateType();
1236 for (
size_t j = i + 1; j < operations.size(); ++j) {
1237 auto &nextOp = operations[j];
1238 if (!nextOp->CanAffectQuantumState())
continue;
1240 const auto nextQubits = nextOp->AffectedQubits();
1242 bool hasQubit =
false;
1244 for (
auto q : nextQubits)
1245 if (q == qubits[0] || q == qubits[1] || q == qubits[2]) {
1253 else if (nextQubits.size() != 3)
1258 (qubits[0] != nextQubits[0] ||
1259 !((qubits[1] == nextQubits[1] &&
1260 qubits[2] == nextQubits[2]) ||
1261 (qubits[1] == nextQubits[2] &&
1262 qubits[2] == nextQubits[1]))))
1265 (qubits[2] != nextQubits[2] ||
1266 !(qubits[1] == nextQubits[1] &&
1267 qubits[2] == nextQubits[2]) ||
1268 !(qubits[1] == nextQubits[2] &&
1269 qubits[2] == nextQubits[1])))
1272 const auto nextType = nextOp->GetType();
1276 const auto &nextGate =
1277 std::static_pointer_cast<ThreeQubitsGate<Time>>(nextOp);
1278 if (nextGate->GetGateType() == gateType) {
1279 nextOp = std::make_shared<NoOperation<Time>>();
1288 if (!found) newops.push_back(op);
1292 newops.push_back(op);
1296 newops.push_back(op);
1298 newops.push_back(op);
1301 operations.swap(newops);
1313 newops.reserve(operations.size());
1317 std::unordered_map<Types::qubit_t, std::vector<OperationPtr>> qubitOps;
1319 std::vector<OperationPtr> lastOps(qubitsNo);
1321 std::unordered_map<OperationPtr, std::unordered_set<OperationPtr>>
1324 for (
const auto &op : operations) {
1325 std::unordered_set<OperationPtr> dependencies;
1327 const auto cbits = op->AffectedBits();
1328 for (
auto c : cbits) {
1329 const auto lastOp = lastOps[c];
1330 if (lastOp) dependencies.insert(lastOp);
1333 const auto qubits = op->AffectedQubits();
1334 for (
auto q : qubits) {
1335 qubitOps[q].push_back(op);
1337 const auto lastOp = lastOps[q];
1338 if (lastOp) dependencies.insert(lastOp);
1343 for (
auto c : cbits) lastOps[c] = op;
1345 dependenciesMap[op] = dependencies;
1349 std::vector<Types::qubit_t> indices(qubitsNo, 0);
1351 while (!dependenciesMap.empty()) {
1356 for (
size_t q = 0; q < qubitsNo; ++q) {
1357 if (qubitOps.find(q) ==
1362 const auto &ops = qubitOps[q];
1363 const auto &op = ops[indices[q]];
1368 bool hasDependencies =
false;
1370 for (
const auto &opd : dependenciesMap[op])
1371 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1372 hasDependencies =
true;
1376 if (!hasDependencies) {
1384 dependenciesMap.erase(nextOp);
1387 for (
auto q : qubits) {
1389 if (indices[q] >= qubitOps[q].
size()) qubitOps.erase(q);
1392 newops.emplace_back(std::move(nextOp));
1398 if (qubitOps.find(q) ==
1403 const auto &ops = qubitOps[q];
1404 const auto &op = ops[indices[q]];
1406 bool hasDependencies =
false;
1408 for (
const auto &opd : dependenciesMap[op])
1409 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1410 hasDependencies =
true;
1414 if (!hasDependencies) {
1421 dependenciesMap.erase(nextOp);
1424 for (
auto q : qubits) {
1426 if (indices[q] >= qubitOps[q].
size()) qubitOps.erase(q);
1429 newops.emplace_back(std::move(nextOp));
1433 assert(newops.size() == operations.size());
1435 operations.swap(newops);
1447 std::pair<std::vector<size_t>, std::vector<Time>>
GetDepth()
const {
1452 std::vector<Time> qubitTimes(qubitsNo, 0);
1453 std::vector<size_t> qubitDepths(qubitsNo, 0);
1455 std::unordered_map<size_t, size_t> fromQubits;
1457 for (
const auto &op : operations) {
1458 const auto qbits = op->AffectedQubits();
1459 const auto delay = op->GetDelay();
1463 for (
auto q : qbits) {
1464 qubitTimes[q] += delay;
1466 if (qubitTimes[q] > maxTime) maxTime = qubitTimes[q];
1467 if (qubitDepths[q] > maxDepth) maxDepth = qubitDepths[q];
1470 const auto t = op->GetType();
1471 std::vector<size_t> condbits;
1479 condbits = op->AffectedBits();
1481 for (
auto bit : condbits) {
1482 if (fromQubits.find(bit) != fromQubits.end()) bit = fromQubits[bit];
1485 for (
auto q : qbits) {
1491 if (found || bit >= qubitsNo)
continue;
1493 qubitTimes[bit] += delay;
1495 if (qubitTimes[bit] > maxTime) maxTime = qubitTimes[bit];
1496 if (qubitDepths[bit] > maxDepth) maxDepth = qubitDepths[bit];
1500 const auto condMeas =
1501 std::static_pointer_cast<ConditionalMeasurement<Time>>(op);
1502 const auto meas = condMeas->GetOperation();
1503 const auto measQubits = meas->AffectedQubits();
1504 const auto measBits = meas->AffectedBits();
1506 for (
size_t i = 0; i < measQubits.size(); ++i) {
1507 if (i < measBits.size())
1508 fromQubits[measBits[i]] = measQubits[i];
1510 fromQubits[measQubits[i]] = measQubits[i];
1514 condbits = op->AffectedBits();
1516 for (
size_t i = 0; i < qbits.size(); ++i) {
1517 if (i < condbits.size())
1518 fromQubits[condbits[i]] = qbits[i];
1520 fromQubits[qbits[i]] = qbits[i];
1524 for (
auto q : qbits) {
1525 qubitTimes[q] = maxTime;
1526 qubitDepths[q] = maxDepth;
1529 for (
auto bit : condbits) {
1530 qubitTimes[bit] = maxTime;
1531 qubitDepths[bit] = maxDepth;
1535 return std::make_pair(qubitDepths, qubitTimes);
1548 auto [qubitDepths, qubitTimes] =
GetDepth();
1551 size_t maxDepth = 0;
1552 for (
size_t qubit = 0; qubit < qubitDepths.size(); ++qubit) {
1553 if (qubitTimes[qubit] > maxTime) maxTime = qubitTimes[qubit];
1554 if (qubitDepths[qubit] > maxDepth) maxDepth = qubitDepths[qubit];
1557 return std::make_pair(maxDepth, maxTime);
1577 if (pos >= operations.size())
return nullptr;
1579 return operations[pos];
1598 newops.reserve(operations.size());
1600 for (
const auto &op : operations) {
1601 const auto qubits = op->AffectedQubits();
1602 bool containsOutsideQubits =
false;
1603 bool containsInsideQubits =
false;
1604 for (
const auto q : qubits) {
1605 if (q < startQubit || q > endQubit) {
1606 containsOutsideQubits =
true;
1607 if (containsInsideQubits)
break;
1609 containsInsideQubits =
true;
1610 if (containsOutsideQubits)
break;
1614 if (containsInsideQubits) {
1615 if (containsOutsideQubits)
1616 throw std::runtime_error(
1617 "Cannot cut the circuit with the specified interval");
1618 newops.emplace_back(op->Clone());
1622 return std::make_shared<Circuit<Time>>(newops);
1636 std::unordered_set<Types::qubit_t> measuredQubits;
1637 std::unordered_set<Types::qubit_t> affectedQubits;
1638 std::unordered_set<Types::qubit_t> resetQubits;
1640 for (
const auto &op : operations) {
1641 const auto qubits = op->AffectedQubits();
1644 for (
const auto qbit : qubits)
1645 if (resetQubits.find(qbit) !=
1659 measuredQubits.insert(qubits.begin(), qubits.end());
1667 for (
const auto qbit : qubits) {
1672 if (affectedQubits.find(qbit) != affectedQubits.end() ||
1673 measuredQubits.find(qbit) != measuredQubits.end())
1674 resetQubits.insert(qbit);
1676 affectedQubits.insert(qbit);
1679 for (
const auto qbit : qubits) {
1680 if (measuredQubits.find(qbit) !=
1685 if (resetQubits.find(qbit) !=
1690 affectedQubits.insert(qbit);
1713 const std::shared_ptr<Simulators::ISimulator> &sim,
1715 std::vector<bool> executedOps;
1716 executedOps.reserve(operations.size());
1718 std::unordered_set<Types::qubit_t> measuredQubits;
1719 std::unordered_set<Types::qubit_t> affectedQubits;
1721 bool executionStopped =
false;
1723 for (
size_t i = 0; i < operations.size(); ++i) {
1724 auto &op = operations[i];
1725 const auto qubits = op->AffectedQubits();
1727 bool executed =
false;
1733 measuredQubits.insert(qubits.begin(), qubits.end());
1738 for (
auto qubit : qubits)
1739 if (affectedQubits.find(qubit) != affectedQubits.end()) {
1746 op->Execute(sim, state);
1747 if (curMaxBondDim) {
1748 const auto bondDim = sim->GetCurrentMaxBondDimension();
1749 if (bondDim > *curMaxBondDim) *curMaxBondDim = bondDim;
1753 measuredQubits.insert(qubits.begin(), qubits.end());
1756 const auto bits = op->AffectedBits();
1771 for (
auto bit : bits)
1772 if (measuredQubits.find(bit) != measuredQubits.end()) {
1777 for (
auto qubit : qubits)
1778 if (measuredQubits.find(qubit) != measuredQubits.end()) {
1786 op->Execute(sim, state);
1787 if (curMaxBondDim) {
1788 const auto bondDim = sim->GetCurrentMaxBondDimension();
1789 if (bondDim > *curMaxBondDim) *curMaxBondDim = bondDim;
1796 measuredQubits.insert(bits.begin(), bits.end());
1797 measuredQubits.insert(qubits.begin(), qubits.end());
1801 affectedQubits.insert(qubits.begin(), qubits.end());
1804 executionStopped =
true;
1805 if (sim && sim->GetSimulationType() ==
1809 sim->SetGatesCounter(sim->GetGatesCounter() + 1);
1811 if (executionStopped) executedOps.emplace_back(executed);
1834 const std::vector<bool> &executedOps,
size_t* curMaxBondDim =
nullptr)
const {
1841 const size_t dif = operations.size() - executedOps.size();
1843 for (
size_t i = dif; i < operations.size(); ++i)
1844 if (!executedOps[i - dif]) {
1845 operations[i]->Execute(sim, state);
1846 if (curMaxBondDim) {
1847 const auto bondDim = sim->GetCurrentMaxBondDimension();
1848 if (bondDim > *curMaxBondDim) *curMaxBondDim = bondDim;
1866 std::vector<bool> &executedOps)
const {
1867 if (executedOps.empty()) {
1868 executedOps.resize(
size(),
false);
1869 return std::make_shared<Circuit<Time>>(operations);
1873 newops.reserve(operations.size());
1875 const size_t dif = operations.size() - executedOps.size();
1876 for (
size_t i = dif; i < operations.size(); ++i)
1877 if (!executedOps[i - dif]) newops.emplace_back(operations[i]);
1879 std::vector<bool> newExecutedOps(newops.size(),
false);
1880 executedOps.swap(newExecutedOps);
1882 return std::make_shared<Circuit<Time>>(newops);
1890 const std::vector<bool> &executedOps,
bool sort =
true)
const {
1891 const size_t dif = operations.size() - executedOps.size();
1892 std::vector<std::pair<Types::qubit_t, size_t>> measurements;
1893 measurements.reserve(dif);
1895 for (
size_t i = dif; i < operations.size(); ++i)
1896 if (!executedOps[i - dif] &&
1899 std::static_pointer_cast<MeasurementOperation<Time>>(operations[i]);
1900 const auto &qubits = measOp->GetQubits();
1901 const auto &bits = measOp->GetBitsIndices();
1903 for (
size_t j = 0; j < qubits.size(); ++j)
1904 measurements.emplace_back(qubits[j], bits[j]);
1910 measurements.begin(), measurements.end(),
1911 [](
const auto &p1,
const auto &p2) { return p1.first < p2.first; });
1913 return std::make_shared<MeasurementOperation<Time>>(measurements);
1925 for (
const auto &op : operations)
1948 for (
const auto &op : operations) {
1949 const auto qubits = op->AffectedQubits();
1950 if (qubits.size() <= 1)
continue;
1952 if (qubits.size() == 2) {
1953 if (std::abs(qubits[0] - qubits[1]) != 1)
return false;
1958 for (
size_t i = 1; i < qubits.size(); ++i) {
1959 if (qubits[i] < minQubit)
1960 minQubit = qubits[i];
1961 else if (qubits[i] > maxQubit)
1962 maxQubit = qubits[i];
1965 if (maxQubit - minQubit >= qubits.size())
return false;
1980 std::unordered_map<Types::qubit_t, size_t> qubits;
1981 std::unordered_map<Types::qubit_t, Types::qubits_vector> lastQubits;
1983 for (
const auto &op : operations) {
1984 const auto q = op->AffectedQubits();
1987 if (q.size() <= 1)
continue;
1989 bool allInTheLastQubits =
true;
1991 for (
const auto qubit : q) {
1992 if (lastQubits.find(qubit) == lastQubits.end()) {
1993 allInTheLastQubits =
false;
1996 const auto &lastQ = lastQubits[qubit];
1998 for (
const auto q1 : q)
1999 if (std::find(lastQ.cbegin(), lastQ.cend(), q1) == lastQ.cend()) {
2000 allInTheLastQubits =
false;
2004 if (!allInTheLastQubits)
break;
2008 if (allInTheLastQubits)
continue;
2010 for (
const auto qubit : q) {
2011 if (qubits[qubit] > 1)
2016 lastQubits[qubit] = q;
2033 for (
const auto &op : operations)
2034 if (!op->IsClifford())
return false;
2049 size_t cliffordOps = 0;
2050 for (
const auto &op : operations)
2051 if (op->IsClifford()) ++cliffordOps;
2053 return static_cast<double>(cliffordOps) / operations.size();
2066 std::unordered_set<Types::qubit_t> cliffordQubits;
2067 std::unordered_set<Types::qubit_t> nonCliffordQubits;
2069 for (
const auto &op : operations) {
2070 const auto qubits = op->AffectedQubits();
2071 if (op->IsClifford()) {
2072 for (
const auto q : qubits) cliffordQubits.insert(q);
2074 for (
const auto q : qubits) nonCliffordQubits.insert(q);
2078 for (
const auto q : nonCliffordQubits) cliffordQubits.erase(q);
2080 return cliffordQubits;
2093 std::vector<std::shared_ptr<Circuit<Time>>> circuits;
2097 std::unordered_map<Types::qubit_t, std::unordered_set<Types::qubit_t>>
2100 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitCircuitMap;
2105 for (
auto qubit : allQubits) {
2106 circuitsMap[qubit] = std::unordered_set<Types::qubit_t>{qubit};
2107 qubitCircuitMap[qubit] = qubit;
2112 for (
const auto &op : operations) {
2113 const auto qubits = op->AffectedQubits();
2115 if (qubits.empty())
continue;
2117 auto qubitIt = qubits.cbegin();
2118 auto firstQubit = *qubitIt;
2120 auto firstQubitCircuit = qubitCircuitMap[firstQubit];
2124 for (; qubitIt != qubits.cend(); ++qubitIt) {
2125 auto qubit = *qubitIt;
2128 auto qubitCircuit = qubitCircuitMap[qubit];
2132 if (firstQubitCircuit != qubitCircuit) {
2134 circuitsMap[firstQubitCircuit].insert(
2135 circuitsMap[qubitCircuit].
begin(),
2136 circuitsMap[qubitCircuit].
end());
2139 for (
auto q : circuitsMap[qubitCircuit])
2140 qubitCircuitMap[q] = firstQubitCircuit;
2143 circuitsMap.erase(qubitCircuit);
2148 size_t circSize = 1ULL;
2150 circSize = std::max(circSize,
static_cast<size_t>(circuitsMap.size()));
2151 circuits.resize(circSize);
2153 for (
size_t i = 0; i < circuits.size(); ++i)
2156 std::unordered_map<Types::qubit_t, size_t> qubitsSetsToCircuit;
2158 size_t circuitNo = 0;
2159 for (
const auto &[
id, qubitSet] : circuitsMap) {
2160 qubitsSetsToCircuit[id] = circuitNo;
2167 for (
const auto &op : operations) {
2168 const auto qubits = op->AffectedQubits();
2170 if (qubits.empty()) {
2171 circuits[0]->AddOperation(op->Clone());
2175 const auto circ = qubitsSetsToCircuit[qubitCircuitMap[*qubits.cbegin()]];
2177 circuits[circ]->AddOperation(op->Clone());
2190 std::vector<std::shared_ptr<Circuits::Circuit<Time>>>
ToLayers()
const {
2191 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2194 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2195 std::unordered_map<size_t, size_t> classicalBitLayer;
2203 if (op->CanAffectQuantumState()) {
2204 const auto qubits = op->AffectedQubits();
2205 size_t maxLevel = 0;
2209 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2211 if (layers.size() < qubitsUsed[qbit]) {
2212 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2213 layers.push_back(std::move(circ));
2217 if (op->IsConditional()) {
2218 const auto bits = op->AffectedBits();
2219 for (
const auto bit : bits)
2220 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2226 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2229 while (layers.size() <= layerIdx)
2232 layers[layerIdx]->AddOperation(op->Clone());
2234 const auto writtenBits = op->AffectedBits();
2235 if (!writtenBits.empty() && !op->IsConditional()) {
2236 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2237 for (
const auto bit : writtenBits)
2238 classicalBitLayer[bit] =
2239 std::max(classicalBitLayer[bit], writtenLevel);
2243 layers.back()->AddOperation(op->Clone());
2259 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2262 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2263 std::unordered_map<size_t, size_t> classicalBitLayer;
2271 if (op->CanAffectQuantumState()) {
2272 const auto qubits = op->AffectedQubits();
2273 size_t maxLevel = 0;
2277 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2279 if (layers.size() < qubitsUsed[qbit]) {
2280 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2281 layers.push_back(std::move(circ));
2285 if (op->IsConditional()) {
2286 const auto bits = op->AffectedBits();
2287 for (
const auto bit : bits)
2288 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2294 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2297 while (layers.size() <= layerIdx)
2300 layers[layerIdx]->AddOperation(op);
2302 const auto writtenBits = op->AffectedBits();
2303 if (!writtenBits.empty() && !op->IsConditional()) {
2304 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2305 for (
const auto bit : writtenBits)
2306 classicalBitLayer[bit] =
2307 std::max(classicalBitLayer[bit], writtenLevel);
2311 layers.back()->AddOperation(op);
2329 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2332 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2333 std::unordered_map<size_t, size_t> classicalBitLayer;
2341 if (op->CanAffectQuantumState()) {
2342 const auto qubits = op->AffectedQubits();
2343 size_t maxLevel = 0;
2346 if (qubits.size() > 1) ++qubitsUsed[qbit];
2347 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2349 if (layers.size() < qubitsUsed[qbit]) {
2350 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2351 layers.push_back(std::move(circ));
2355 if (op->IsConditional()) {
2356 const auto bits = op->AffectedBits();
2357 for (
const auto bit : bits)
2358 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2364 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2367 while (layers.size() <= layerIdx)
2370 layers[layerIdx]->AddOperation(op->Clone());
2372 const auto writtenBits = op->AffectedBits();
2373 if (!writtenBits.empty() && !op->IsConditional()) {
2374 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2375 for (
const auto bit : writtenBits)
2376 classicalBitLayer[bit] =
2377 std::max(classicalBitLayer[bit], writtenLevel);
2381 layers.back()->AddOperation(op->Clone());
2397 std::vector<std::shared_ptr<Circuits::Circuit<Time>>>
2399 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2402 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2407 std::unordered_map<size_t, size_t> classicalBitLayer;
2415 if (op->CanAffectQuantumState()) {
2416 const auto qubits = op->AffectedQubits();
2417 size_t maxLevel = 0;
2420 if (qubits.size() > 1) ++qubitsUsed[qbit];
2421 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2423 if (layers.size() < qubitsUsed[qbit]) {
2424 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2425 layers.push_back(std::move(circ));
2431 if (op->IsConditional()) {
2432 const auto bits = op->AffectedBits();
2433 for (
const auto bit : bits)
2434 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2440 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2443 while (layers.size() <= layerIdx)
2446 layers[layerIdx]->AddOperation(op);
2450 const auto writtenBits = op->AffectedBits();
2451 if (!writtenBits.empty() && !op->IsConditional()) {
2452 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2453 for (
const auto bit : writtenBits)
2454 classicalBitLayer[bit] =
2455 std::max(classicalBitLayer[bit], writtenLevel);
2459 layers.back()->AddOperation(op);
2476 auto circuit{std::make_shared<Circuits::Circuit<Time>>()};
2478 for (
const auto &layer : layers)
2479 circuit->AddOperations(layer->GetOperations());
2494 if (op->IsBranching())
return true;
2571 return operations.crbegin();
2588 auto size()
const {
return operations.size(); }
2596 auto empty()
const {
return operations.empty(); }
2614 const auto &
operator[](
size_t pos)
const {
return operations[pos]; }
2624 if (
size < operations.size()) operations.resize(
size);
2638 void ReplaceThreeQubitAndSwapGates(
bool onlyThreeQubits =
false) {
2647 std::vector<std::shared_ptr<IOperation<Time>>> newops;
2648 newops.reserve(operations.size());
2652 std::shared_ptr<IQuantumGate<Time>> gate =
2653 std::static_pointer_cast<IQuantumGate<Time>>(op);
2655 if (NeedsConversion(gate, onlyThreeQubits)) {
2656 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2657 ConvertGate(gate, onlyThreeQubits);
2658 newops.insert(newops.end(), newgates.begin(), newgates.end());
2660 newops.push_back(op);
2662 std::shared_ptr<ConditionalGate<Time>> condgate =
2663 std::static_pointer_cast<ConditionalGate<Time>>(op);
2664 std::shared_ptr<IQuantumGate<Time>> gate =
2665 std::static_pointer_cast<IQuantumGate<Time>>(
2666 condgate->GetOperation());
2668 if (NeedsConversion(gate, onlyThreeQubits)) {
2669 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2670 ConvertGate(gate, onlyThreeQubits);
2671 std::shared_ptr<ICondition> cond = condgate->GetCondition();
2673 for (
auto gate : newgates)
2675 std::make_shared<ConditionalGate<Time>>(gate, cond));
2677 newops.push_back(op);
2679 newops.push_back(op);
2682 operations.swap(newops);
2694 static bool NeedsConversion(
const std::shared_ptr<IQuantumGate<Time>> &gate,
2695 bool onlyThreeQubits =
false) {
2696 const bool hasThreeQubits = gate->GetNumQubits() == 3;
2697 if (onlyThreeQubits)
return hasThreeQubits;
2699 return hasThreeQubits ||
2715 static std::vector<std::shared_ptr<IGateOperation<Time>>> ConvertGate(
2716 std::shared_ptr<IQuantumGate<Time>> &gate,
bool onlyThreeQubits =
false) {
2719 std::vector<std::shared_ptr<IGateOperation<Time>>> newops;
2721 if (gate->GetNumQubits() == 3) {
2724 const size_t q1 = gate->GetQubit(0);
2725 const size_t q2 = gate->GetQubit(1);
2726 const size_t q3 = gate->GetQubit(2);
2729 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2730 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2731 newops.push_back(std::make_shared<CSxDagGate<Time>>(q2, q3));
2732 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2733 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2735 const size_t q1 = gate->GetQubit(0);
2736 const size_t q2 = gate->GetQubit(1);
2737 const size_t q3 = gate->GetQubit(2);
2741 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2743 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2744 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2745 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2747 newops.push_back(std::make_shared<PhaseGate<Time>>(q2, -M_PI_2));
2749 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2750 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2751 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2753 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2755 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2757 newops.push_back(gate);
2758 }
else if (!onlyThreeQubits &&
2761 const size_t q1 = gate->GetQubit(0);
2762 const size_t q2 = gate->GetQubit(1);
2766 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2767 newops.push_back(std::make_shared<CXGate<Time>>(q2, q1));
2768 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2770 newops.push_back(gate);
2791template <
typename Time = Types::time_type>
2799 std::vector<OperationPtr>;
2859 if (approximateParamsCheck) {
2860 const auto params1 = std::static_pointer_cast<IQuantumGate<Time>>(
2863 const auto params2 = std::static_pointer_cast<IQuantumGate<Time>>(
2866 if (params1.size() != params2.size())
return false;
2868 for (
size_t j = 0; j < params1.size(); ++j)
2869 if (std::abs(params1[j] - params2[j]) > paramsEpsilon)
2895 const auto leftCondition =
2896 std::static_pointer_cast<IConditionalOperation<Time>>(
2899 const auto rightCondition =
2900 std::static_pointer_cast<IConditionalOperation<Time>>(
2903 if (leftCondition->GetBitsIndices() !=
2904 rightCondition->GetBitsIndices())
2907 const auto leftEqCondition =
2908 std::static_pointer_cast<EqualCondition>(leftCondition);
2909 const auto rightEqCondition =
2910 std::static_pointer_cast<EqualCondition>(rightCondition);
2911 if (!leftEqCondition || !rightEqCondition)
return false;
2913 if (leftEqCondition->GetAllBits() != rightEqCondition->GetAllBits())
2918 std::static_pointer_cast<IConditionalOperation<Time>>(
2921 const auto rightOp =
2922 std::static_pointer_cast<IConditionalOperation<Time>>(
2932 if (leftCircuit != rightCircuit)
return false;
2946 std::static_pointer_cast<QuantumChannelOperation<Time>>(
2949 std::static_pointer_cast<QuantumChannelOperation<Time>>(
2951 if (left->AffectedQubits() != right->AffectedQubits() ||
2952 !left->GetChannel().IsApprox(
2953 right->GetChannel(),
2954 approximateParamsCheck ? paramsEpsilon : 0.0))
2959 std::static_pointer_cast<Delay<Time>>(
2962 std::static_pointer_cast<Delay<Time>>(
2964 if (left->GetQubit() != right->GetQubit())
return false;
2965 if (approximateParamsCheck) {
2966 if (std::abs(left->GetDuration() - right->GetDuration()) > paramsEpsilon)
2968 }
else if (left->GetDuration() != right->GetDuration()) {
3031 bool approximateParamsCheck =
3033 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.
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::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< 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.
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::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.
std::vector< OperationPtr > OperationsVector
The vector of operations.
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.
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.
std::unordered_map< Types::qubit_t, Types::qubit_t > BitMapping
The (qu)bit mapping for remapping.
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.
size_t GetMaxQubitIndex() const
Returns the max qubit id for all operations.
std::unordered_map< std::vector< bool >, size_t > ExecuteResults
The results of the execution of the circuit.
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.
typename OperationsVector::const_reverse_iterator const_reverse_iterator
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.
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.
std::vector< OperationPtr > OperationsVector
The vector of operations.
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.
Delay (idle) operation class.
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'
@ 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
@ kMatrixProductState
matrix product state 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.