22#define _USE_MATH_DEFINES
45template <
typename Time = Types::time_type>
49 std::unordered_map<std::vector<bool>,
61 std::vector<OperationPtr>;
63 using value_type =
typename OperationsVector::value_type;
65 using pointer =
typename OperationsVector::pointer;
67 using reference =
typename OperationsVector::reference;
69 using size_type =
typename OperationsVector::size_type;
72 using iterator =
typename OperationsVector::iterator;
76 typename OperationsVector::const_reverse_iterator;
96 void Execute(
const std::shared_ptr<Simulators::ISimulator> &sim,
101 for (
const auto &op : operations) op->Execute(sim, state);
132 if (index >= operations.size())
return;
133 operations[index] = op;
153 operations.insert(operations.end(), ops.begin(), ops.end());
180 void Clear() { operations.clear(); }
191 for (
auto &op : operations) newops.emplace_back(op->Clone());
193 return std::make_shared<Circuit<Time>>(newops);
206 for (
auto &op : operations) newops.push_back(op);
208 return std::make_shared<Circuit<Time>>(newops);
222 const BitMapping &bitsMap = {})
const override {
225 for (
const auto &op : operations)
226 newops.emplace_back(op->Remap(qubitsMap, bitsMap));
228 return std::make_shared<Circuit<Time>>(newops);
245 size_t &nrCbits)
const {
253 for (
const auto &op : operations) {
254 const auto affectedBits = op->AffectedBits();
255 const auto affectedQubits = op->AffectedQubits();
257 for (
const auto qubit : affectedQubits) {
258 const auto it = newQubitsMap.find(qubit);
259 if (it == newQubitsMap.end()) {
260 newQubitsMap[qubit] = nrQubits;
265 for (
const auto bit : affectedBits) {
266 const auto it = newBitsMap.find(bit);
267 if (it == newBitsMap.end()) {
268 newBitsMap[bit] = nrCbits;
269 reverseBitsMap[nrCbits] = bit;
274 newops.emplace_back(op->Remap(newQubitsMap, newBitsMap));
277 return std::make_shared<Circuit<Time>>(newops);
296 bool ignoreNotMapped =
false,
300 if (!ignoreNotMapped && sz == 0) {
301 for (
const auto &[from, to] : bitsMap)
302 if (to > sz) sz = to;
307 for (
const auto &res : results) {
310 mappedState.Remap(bitsMap, ignoreNotMapped,
311 ignoreNotMapped ? bitsMap.size() : sz);
312 newResults[mappedState.GetAllBits()] += res.second;
328 for (
const auto &res : newResults) results[res.first] += res.second;
348 bool ignoreNotMapped =
true,
350 if (!ignoreNotMapped && sz == 0) {
351 for (
const auto &[from, to] : bitsMap)
352 if (to > sz) sz = to;
357 for (
const auto &res : newResults) {
360 mappedState.Remap(bitsMap, ignoreNotMapped,
361 ignoreNotMapped ? bitsMap.size() : sz);
362 results[mappedState.GetAllBits()] += res.second;
391 ReplaceThreeQubitAndSwapGates();
427 for (
size_t i = 0; i < operations.size(); ++i) {
428 const auto op = operations[i];
430 newops.emplace_back(op);
436 std::unordered_set<size_t> bits;
437 std::unordered_map<size_t, Types::qubit_t> measQubits;
438 std::unordered_map<size_t, Time> measDelays;
440 auto affectedBits = op->AffectedBits();
441 auto affectedQubits = op->AffectedQubits();
443 for (
size_t q = 0; q < affectedQubits.size(); ++q) {
444 bits.insert(affectedBits[q]);
445 measQubits[affectedBits[q]] = affectedQubits[q];
446 measDelays[affectedBits[q]] = op->GetDelay();
450 for (; j < operations.size(); ++j) {
451 const auto op2 = operations[j];
455 affectedQubits = op2->AffectedQubits();
458 std::static_pointer_cast<MeasurementOperation<Time>>(op2);
459 affectedBits = meas->GetBitsIndices();
460 for (
size_t q = 0; q < affectedBits.size(); ++q) {
461 bits.insert(affectedBits[q]);
462 measQubits[affectedBits[q]] = affectedQubits[q];
463 measDelays[affectedBits[q]] = op2->GetDelay();
471 for (; j < operations.size(); ++j) {
472 const auto op2 = operations[j];
477 std::static_pointer_cast<IConditionalOperation<Time>>(op2);
478 const auto condbits = condop->AffectedBits();
479 for (
const auto bit : condbits)
480 if (bits.find(bit) != bits.end()) {
482 std::vector{std::make_pair(measQubits[bit], bit)},
487 if (bits.empty())
break;
491 for (
auto bit : bits)
493 std::vector{std::make_pair(measQubits[bit], bit)},
497 operations.swap(newops);
509 for (
const auto &op : operations) {
510 const auto qbits = op->AffectedQubits();
526 size_t mn = std::numeric_limits<size_t>::max();
527 for (
const auto &op : operations) {
528 const auto qbits = op->AffectedQubits();
545 for (
const auto &op : operations) {
546 const auto cbits = op->AffectedBits();
562 size_t mn = std::numeric_limits<size_t>::max();
563 for (
const auto &op : operations) {
564 const auto cbits = op->AffectedBits();
582 std::set<size_t> qubits;
583 for (
const auto &op : operations) {
584 const auto qbits = op->AffectedQubits();
585 qubits.insert(qbits.begin(), qbits.end());
599 std::set<size_t> cbits;
600 for (
const auto &op : operations) {
601 const auto bits = op->AffectedBits();
602 cbits.insert(bits.begin(), bits.end());
618 qubitsVec.reserve(qubits.size());
620 for (
auto q : qubits) qubitsVec.emplace_back(q);
634 std::vector<size_t> bitsVec;
635 bitsVec.reserve(bits.size());
637 for (
auto b : bits) bitsVec.emplace_back(b);
652 for (
const auto &op : operations)
653 if (op->NeedsEntanglementForDistribution())
return true;
666 for (
const auto &op : operations)
667 if (op->CanAffectQuantumState())
return true;
680 std::unordered_map<size_t, OperationPtr> lastOps;
682 for (
const auto &op : operations) {
683 const auto qbits = op->AffectedQubits();
684 for (
auto q : qbits) lastOps[q] = op;
698 std::unordered_map<size_t, OperationPtr> firstOps;
700 for (
const auto &op : operations) {
701 const auto qbits = op->AffectedQubits();
702 for (
auto q : qbits) {
703 if (firstOps.find(q) == firstOps.end()) firstOps[q] = op;
721 for (
const auto &[q, op] : GetLastOps)
725 operations.emplace_back(
740 for (
const auto &[q, op] : GetFirstOps)
816 std::vector<std::shared_ptr<IOperation<Time>>> newops;
817 newops.reserve(operations.size());
819 for (
int i = 0; i < static_cast<int>(operations.size()); ++i) {
820 const std::shared_ptr<IOperation<Time>> &op = operations[i];
822 const auto type = op->GetType();
826 std::shared_ptr<IQuantumGate<Time>> gate =
827 std::static_pointer_cast<IQuantumGate<Time>>(op);
828 const auto qubits = gate->AffectedQubits();
830 if (qubits.size() == 1) {
833 auto gateType = gate->GetGateType();
834 bool replace =
false;
847 if (!optimizeRotationGates) {
848 newops.push_back(op);
893 for (
size_t j = i + 1; j < operations.size(); ++j) {
894 auto &nextOp = operations[j];
895 if (!nextOp->CanAffectQuantumState())
continue;
897 const auto nextQubits = nextOp->AffectedQubits();
898 bool hasQubit =
false;
900 for (
auto q : nextQubits)
901 if (q == qubits[0]) {
909 else if (nextQubits.size() != 1)
913 const auto nextType = nextOp->GetType();
917 const auto &nextGate =
918 std::static_pointer_cast<SingleQubitGate<Time>>(nextOp);
919 if (nextGate->GetGateType() == gateType) {
921 const auto params1 = gate->GetParams();
922 const auto params2 = nextGate->GetParams();
924 const double param = params1[0] + params2[0];
926 gate->GetDelay() + nextGate->GetDelay();
930 qubits[0], param, delay));
933 qubits[0], param, delay));
936 qubits[0], param, delay));
939 qubits[0], param, delay));
941 nextOp = std::make_shared<NoOperation<Time>>();
946 nextGate->GetGateType() ==
949 nextGate->GetGateType() ==
954 const auto delay = gate->GetDelay() + nextGate->GetDelay();
957 nextOp = std::make_shared<NoOperation<Time>>();
962 nextGate->GetGateType() ==
965 nextGate->GetGateType() ==
970 const auto delay = gate->GetDelay() + nextGate->GetDelay();
973 nextOp = std::make_shared<NoOperation<Time>>();
978 nextGate->GetGateType() ==
982 const auto delay = gate->GetDelay() + nextGate->GetDelay();
985 nextOp = std::make_shared<NoOperation<Time>>();
990 nextGate->GetGateType() ==
994 const auto delay = gate->GetDelay() + nextGate->GetDelay();
997 nextOp = std::make_shared<NoOperation<Time>>();
1002 (nextGate->GetGateType() ==
1004 nextGate->GetGateType() ==
1006 nextGate->GetGateType() ==
1008 nextGate->GetGateType() ==
1010 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1013 param2 = 0.5 * M_PI;
1014 else if (nextGate->GetGateType() ==
1016 param2 = -0.5 * M_PI;
1017 else if (nextGate->GetGateType() ==
1019 param2 = 0.25 * M_PI;
1021 param2 = -0.25 * M_PI;
1023 const auto param = gate->GetParams()[0] + param2;
1025 qubits[0], param, delay));
1026 nextOp = std::make_shared<NoOperation<Time>>();
1030 }
else if (nextGate->GetGateType() ==
1036 const auto delay = gate->GetDelay() + nextGate->GetDelay();
1039 param1 = -0.5 * M_PI;
1041 param1 = 0.5 * M_PI;
1043 param1 = -0.25 * M_PI;
1045 param1 = 0.25 * M_PI;
1047 const auto param = nextGate->GetParams()[0] + param1;
1049 qubits[0], param, delay));
1050 nextOp = std::make_shared<NoOperation<Time>>();
1059 if (!found) newops.push_back(op);
1063 newops.push_back(op);
1066 }
else if (qubits.size() == 2) {
1067 auto gateType = gate->GetGateType();
1068 bool replace =
false;
1081 if (!optimizeRotationGates) {
1082 newops.push_back(op);
1113 for (
size_t j = i + 1; j < operations.size(); ++j) {
1114 auto &nextOp = operations[j];
1115 if (!nextOp->CanAffectQuantumState())
continue;
1117 const auto nextQubits = nextOp->AffectedQubits();
1119 bool hasQubit =
false;
1121 for (
auto q : nextQubits)
1122 if (q == qubits[0] || q == qubits[1]) {
1130 else if (nextQubits.size() != 2)
1135 !((qubits[0] == nextQubits[0] &&
1136 qubits[1] == nextQubits[1]) ||
1137 (qubits[0] == nextQubits[1] &&
1138 qubits[1] == nextQubits[0])))
1140 else if (!(qubits[0] == nextQubits[0] &&
1141 qubits[1] == nextQubits[1]))
1144 const auto nextType = nextOp->GetType();
1148 const auto &nextGate =
1149 std::static_pointer_cast<TwoQubitsGate<Time>>(nextOp);
1150 if (nextGate->GetGateType() == gateType) {
1152 const auto params1 = gate->GetParams();
1153 const auto params2 = nextGate->GetParams();
1154 const double param = params1[0] + params2[0];
1156 gate->GetDelay() + nextGate->GetDelay();
1160 qubits[0], qubits[1], param, delay));
1163 qubits[0], qubits[1], param, delay));
1166 qubits[0], qubits[1], param, delay));
1169 qubits[0], qubits[1], param, delay));
1171 nextOp = std::make_shared<NoOperation<Time>>();
1182 if (!found) newops.push_back(op);
1186 newops.push_back(op);
1189 }
else if (qubits.size() == 3) {
1190 auto gateType = gate->GetGateType();
1203 for (
size_t j = i + 1; j < operations.size(); ++j) {
1204 auto &nextOp = operations[j];
1205 if (!nextOp->CanAffectQuantumState())
continue;
1207 const auto nextQubits = nextOp->AffectedQubits();
1209 bool hasQubit =
false;
1211 for (
auto q : nextQubits)
1212 if (q == qubits[0] || q == qubits[1] || q == qubits[2]) {
1220 else if (nextQubits.size() != 3)
1225 (qubits[0] != nextQubits[0] ||
1226 !((qubits[1] == nextQubits[1] &&
1227 qubits[2] == nextQubits[2]) ||
1228 (qubits[1] == nextQubits[2] &&
1229 qubits[2] == nextQubits[1]))))
1232 (qubits[2] != nextQubits[2] ||
1233 !(qubits[1] == nextQubits[1] &&
1234 qubits[2] == nextQubits[2]) ||
1235 !(qubits[1] == nextQubits[2] &&
1236 qubits[2] == nextQubits[1])))
1239 const auto nextType = nextOp->GetType();
1243 const auto &nextGate =
1244 std::static_pointer_cast<ThreeQubitsGate<Time>>(nextOp);
1245 if (nextGate->GetGateType() == gateType) {
1246 nextOp = std::make_shared<NoOperation<Time>>();
1255 if (!found) newops.push_back(op);
1259 newops.push_back(op);
1263 newops.push_back(op);
1265 newops.push_back(op);
1268 operations.swap(newops);
1280 newops.reserve(operations.size());
1284 std::unordered_map<Types::qubit_t, std::vector<OperationPtr>> qubitOps;
1286 std::vector<OperationPtr> lastOps(qubitsNo);
1288 std::unordered_map<OperationPtr, std::unordered_set<OperationPtr>>
1291 for (
const auto &op : operations) {
1292 std::unordered_set<OperationPtr> dependencies;
1294 const auto cbits = op->AffectedBits();
1295 for (
auto c : cbits) {
1296 const auto lastOp = lastOps[c];
1297 if (lastOp) dependencies.insert(lastOp);
1300 const auto qubits = op->AffectedQubits();
1301 for (
auto q : qubits) {
1302 qubitOps[q].push_back(op);
1304 const auto lastOp = lastOps[q];
1305 if (lastOp) dependencies.insert(lastOp);
1310 for (
auto c : cbits) lastOps[c] = op;
1312 dependenciesMap[op] = dependencies;
1316 std::vector<Types::qubit_t> indices(qubitsNo, 0);
1318 while (!dependenciesMap.empty()) {
1323 for (
size_t q = 0; q < qubitsNo; ++q) {
1324 if (qubitOps.find(q) ==
1329 const auto &ops = qubitOps[q];
1330 const auto &op = ops[indices[q]];
1335 bool hasDependencies =
false;
1337 for (
const auto &opd : dependenciesMap[op])
1338 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1339 hasDependencies =
true;
1343 if (!hasDependencies) {
1351 dependenciesMap.erase(nextOp);
1354 for (
auto q : qubits) {
1356 if (indices[q] >= qubitOps[q].
size()) qubitOps.erase(q);
1359 newops.emplace_back(std::move(nextOp));
1365 if (qubitOps.find(q) ==
1370 const auto &ops = qubitOps[q];
1371 const auto &op = ops[indices[q]];
1373 bool hasDependencies =
false;
1375 for (
const auto &opd : dependenciesMap[op])
1376 if (dependenciesMap.find(opd) != dependenciesMap.end()) {
1377 hasDependencies =
true;
1381 if (!hasDependencies) {
1388 dependenciesMap.erase(nextOp);
1391 for (
auto q : qubits) {
1393 if (indices[q] >= qubitOps[q].
size()) qubitOps.erase(q);
1396 newops.emplace_back(std::move(nextOp));
1400 assert(newops.size() == operations.size());
1402 operations.swap(newops);
1414 std::pair<std::vector<size_t>, std::vector<Time>>
GetDepth()
const {
1419 std::vector<Time> qubitTimes(qubitsNo, 0);
1420 std::vector<size_t> qubitDepths(qubitsNo, 0);
1422 std::unordered_map<size_t, size_t> fromQubits;
1424 for (
const auto &op : operations) {
1425 const auto qbits = op->AffectedQubits();
1426 const auto delay = op->GetDelay();
1430 for (
auto q : qbits) {
1431 qubitTimes[q] += delay;
1433 if (qubitTimes[q] > maxTime) maxTime = qubitTimes[q];
1434 if (qubitDepths[q] > maxDepth) maxDepth = qubitDepths[q];
1437 const auto t = op->GetType();
1438 std::vector<size_t> condbits;
1446 condbits = op->AffectedBits();
1448 for (
auto bit : condbits) {
1449 if (fromQubits.find(bit) != fromQubits.end()) bit = fromQubits[bit];
1452 for (
auto q : qbits) {
1458 if (found || bit >= qubitsNo)
continue;
1460 qubitTimes[bit] += delay;
1462 if (qubitTimes[bit] > maxTime) maxTime = qubitTimes[bit];
1463 if (qubitDepths[bit] > maxDepth) maxDepth = qubitDepths[bit];
1467 const auto condMeas =
1468 std::static_pointer_cast<ConditionalMeasurement<Time>>(op);
1469 const auto meas = condMeas->GetOperation();
1470 const auto measQubits = meas->AffectedQubits();
1471 const auto measBits = meas->AffectedBits();
1473 for (
size_t i = 0; i < measQubits.size(); ++i) {
1474 if (i < measBits.size())
1475 fromQubits[measBits[i]] = measQubits[i];
1477 fromQubits[measQubits[i]] = measQubits[i];
1481 condbits = op->AffectedBits();
1483 for (
size_t i = 0; i < qbits.size(); ++i) {
1484 if (i < condbits.size())
1485 fromQubits[condbits[i]] = qbits[i];
1487 fromQubits[qbits[i]] = qbits[i];
1491 for (
auto q : qbits) {
1492 qubitTimes[q] = maxTime;
1493 qubitDepths[q] = maxDepth;
1496 for (
auto bit : condbits) {
1497 qubitTimes[bit] = maxTime;
1498 qubitDepths[bit] = maxDepth;
1502 return std::make_pair(qubitDepths, qubitTimes);
1515 auto [qubitDepths, qubitTimes] =
GetDepth();
1518 size_t maxDepth = 0;
1519 for (
size_t qubit = 0; qubit < qubitDepths.size(); ++qubit) {
1520 if (qubitTimes[qubit] > maxTime) maxTime = qubitTimes[qubit];
1521 if (qubitDepths[qubit] > maxDepth) maxDepth = qubitDepths[qubit];
1524 return std::make_pair(maxDepth, maxTime);
1544 if (pos >= operations.size())
return nullptr;
1546 return operations[pos];
1565 newops.reserve(operations.size());
1567 for (
const auto &op : operations) {
1568 const auto qubits = op->AffectedQubits();
1569 bool containsOutsideQubits =
false;
1570 bool containsInsideQubits =
false;
1571 for (
const auto q : qubits) {
1572 if (q < startQubit || q > endQubit) {
1573 containsOutsideQubits =
true;
1574 if (containsInsideQubits)
break;
1576 containsInsideQubits =
true;
1577 if (containsOutsideQubits)
break;
1581 if (containsInsideQubits) {
1582 if (containsOutsideQubits)
1583 throw std::runtime_error(
1584 "Cannot cut the circuit with the specified interval");
1585 newops.emplace_back(op->Clone());
1589 return std::make_shared<Circuit<Time>>(newops);
1603 std::unordered_set<Types::qubit_t> measuredQubits;
1604 std::unordered_set<Types::qubit_t> affectedQubits;
1605 std::unordered_set<Types::qubit_t> resetQubits;
1607 for (
const auto &op : operations) {
1608 const auto qubits = op->AffectedQubits();
1611 for (
const auto qbit : qubits)
1612 if (resetQubits.find(qbit) !=
1626 measuredQubits.insert(qubits.begin(), qubits.end());
1634 for (
const auto qbit : qubits) {
1639 if (affectedQubits.find(qbit) != affectedQubits.end() ||
1640 measuredQubits.find(qbit) != measuredQubits.end())
1641 resetQubits.insert(qbit);
1643 affectedQubits.insert(qbit);
1646 for (
const auto qbit : qubits) {
1647 if (measuredQubits.find(qbit) !=
1652 if (resetQubits.find(qbit) !=
1657 affectedQubits.insert(qbit);
1678 const std::shared_ptr<Simulators::ISimulator> &sim,
1680 std::vector<bool> executedOps;
1681 executedOps.reserve(operations.size());
1683 std::unordered_set<Types::qubit_t> measuredQubits;
1684 std::unordered_set<Types::qubit_t> affectedQubits;
1686 bool executionStopped =
false;
1688 for (
size_t i = 0; i < operations.size(); ++i) {
1689 auto &op = operations[i];
1690 const auto qubits = op->AffectedQubits();
1692 bool executed =
false;
1698 measuredQubits.insert(qubits.begin(), qubits.end());
1703 for (
auto qubit : qubits)
1704 if (affectedQubits.find(qubit) != affectedQubits.end()) {
1710 if (sim) op->Execute(sim, state);
1712 measuredQubits.insert(qubits.begin(), qubits.end());
1715 const auto bits = op->AffectedBits();
1728 for (
auto bit : bits)
1729 if (measuredQubits.find(bit) != measuredQubits.end()) {
1734 for (
auto qubit : qubits)
1735 if (measuredQubits.find(qubit) != measuredQubits.end()) {
1742 if (sim) op->Execute(sim, state);
1747 measuredQubits.insert(bits.begin(), bits.end());
1748 measuredQubits.insert(qubits.begin(), qubits.end());
1752 affectedQubits.insert(qubits.begin(), qubits.end());
1755 executionStopped =
true;
1756 if (sim && sim->GetSimulationType() ==
1760 sim->SetGatesCounter(sim->GetGatesCounter() + 1);
1762 if (executionStopped) executedOps.emplace_back(executed);
1783 const std::vector<bool> &executedOps)
const {
1790 const size_t dif = operations.size() - executedOps.size();
1792 for (
size_t i = dif; i < operations.size(); ++i)
1793 if (!executedOps[i - dif]) operations[i]->Execute(sim, state);
1809 std::vector<bool> &executedOps)
const {
1810 if (executedOps.empty()) {
1811 executedOps.resize(
size(),
false);
1812 return std::make_shared<Circuit<Time>>(operations);
1816 newops.reserve(operations.size());
1818 const size_t dif = operations.size() - executedOps.size();
1819 for (
size_t i = dif; i < operations.size(); ++i)
1820 if (!executedOps[i - dif]) newops.emplace_back(operations[i]);
1822 std::vector<bool> newExecutedOps(newops.size(),
false);
1823 executedOps.swap(newExecutedOps);
1825 return std::make_shared<Circuit<Time>>(newops);
1833 const std::vector<bool> &executedOps,
bool sort =
true)
const {
1834 const size_t dif = operations.size() - executedOps.size();
1835 std::vector<std::pair<Types::qubit_t, size_t>> measurements;
1836 measurements.reserve(dif);
1838 for (
size_t i = dif; i < operations.size(); ++i)
1839 if (!executedOps[i - dif] &&
1842 std::static_pointer_cast<MeasurementOperation<Time>>(operations[i]);
1843 const auto &qubits = measOp->GetQubits();
1844 const auto &bits = measOp->GetBitsIndices();
1846 for (
size_t j = 0; j < qubits.size(); ++j)
1847 measurements.emplace_back(qubits[j], bits[j]);
1853 measurements.begin(), measurements.end(),
1854 [](
const auto &p1,
const auto &p2) { return p1.first < p2.first; });
1856 return std::make_shared<MeasurementOperation<Time>>(measurements);
1868 for (
const auto &op : operations)
1891 for (
const auto &op : operations) {
1892 const auto qubits = op->AffectedQubits();
1893 if (qubits.size() <= 1)
continue;
1895 if (qubits.size() == 2) {
1896 if (std::abs(qubits[0] - qubits[1]) != 1)
return false;
1901 for (
size_t i = 1; i < qubits.size(); ++i) {
1902 if (qubits[i] < minQubit)
1903 minQubit = qubits[i];
1904 else if (qubits[i] > maxQubit)
1905 maxQubit = qubits[i];
1908 if (maxQubit - minQubit >= qubits.size())
return false;
1923 std::unordered_map<Types::qubit_t, size_t> qubits;
1924 std::unordered_map<Types::qubit_t, Types::qubits_vector> lastQubits;
1926 for (
const auto &op : operations) {
1927 const auto q = op->AffectedQubits();
1930 if (q.size() <= 1)
continue;
1932 bool allInTheLastQubits =
true;
1934 for (
const auto qubit : q) {
1935 if (lastQubits.find(qubit) == lastQubits.end()) {
1936 allInTheLastQubits =
false;
1939 const auto &lastQ = lastQubits[qubit];
1941 for (
const auto q1 : q)
1942 if (std::find(lastQ.cbegin(), lastQ.cend(), q1) == lastQ.cend()) {
1943 allInTheLastQubits =
false;
1947 if (!allInTheLastQubits)
break;
1951 if (allInTheLastQubits)
continue;
1953 for (
const auto qubit : q) {
1954 if (qubits[qubit] > 1)
1959 lastQubits[qubit] = q;
1976 for (
const auto &op : operations)
1977 if (!op->IsClifford())
return false;
1992 size_t cliffordOps = 0;
1993 for (
const auto &op : operations)
1994 if (op->IsClifford()) ++cliffordOps;
1996 return static_cast<double>(cliffordOps) / operations.size();
2009 std::unordered_set<Types::qubit_t> cliffordQubits;
2010 std::unordered_set<Types::qubit_t> nonCliffordQubits;
2012 for (
const auto &op : operations) {
2013 const auto qubits = op->AffectedQubits();
2014 if (op->IsClifford()) {
2015 for (
const auto q : qubits) cliffordQubits.insert(q);
2017 for (
const auto q : qubits) nonCliffordQubits.insert(q);
2021 for (
const auto q : nonCliffordQubits) cliffordQubits.erase(q);
2023 return cliffordQubits;
2036 std::vector<std::shared_ptr<Circuit<Time>>> circuits;
2040 std::unordered_map<Types::qubit_t, std::unordered_set<Types::qubit_t>>
2043 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitCircuitMap;
2048 for (
auto qubit : allQubits) {
2049 circuitsMap[qubit] = std::unordered_set<Types::qubit_t>{qubit};
2050 qubitCircuitMap[qubit] = qubit;
2055 for (
const auto &op : operations) {
2056 const auto qubits = op->AffectedQubits();
2058 if (qubits.empty())
continue;
2060 auto qubitIt = qubits.cbegin();
2061 auto firstQubit = *qubitIt;
2063 auto firstQubitCircuit = qubitCircuitMap[firstQubit];
2067 for (; qubitIt != qubits.cend(); ++qubitIt) {
2068 auto qubit = *qubitIt;
2071 auto qubitCircuit = qubitCircuitMap[qubit];
2075 if (firstQubitCircuit != qubitCircuit) {
2077 circuitsMap[firstQubitCircuit].insert(
2078 circuitsMap[qubitCircuit].
begin(),
2079 circuitsMap[qubitCircuit].
end());
2082 for (
auto q : circuitsMap[qubitCircuit])
2083 qubitCircuitMap[q] = firstQubitCircuit;
2086 circuitsMap.erase(qubitCircuit);
2091 size_t circSize = 1ULL;
2093 circSize = std::max(circSize,
static_cast<size_t>(circuitsMap.size()));
2094 circuits.resize(circSize);
2096 for (
size_t i = 0; i < circuits.size(); ++i)
2099 std::unordered_map<Types::qubit_t, size_t> qubitsSetsToCircuit;
2101 size_t circuitNo = 0;
2102 for (
const auto &[
id, qubitSet] : circuitsMap) {
2103 qubitsSetsToCircuit[id] = circuitNo;
2110 for (
const auto &op : operations) {
2111 const auto qubits = op->AffectedQubits();
2113 if (qubits.empty()) {
2114 circuits[0]->AddOperation(op->Clone());
2118 const auto circ = qubitsSetsToCircuit[qubitCircuitMap[*qubits.cbegin()]];
2120 circuits[circ]->AddOperation(op->Clone());
2133 std::vector<std::shared_ptr<Circuits::Circuit<Time>>>
ToLayers()
const {
2134 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2137 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2138 std::unordered_map<size_t, size_t> classicalBitLayer;
2146 if (op->CanAffectQuantumState()) {
2147 const auto qubits = op->AffectedQubits();
2148 size_t maxLevel = 0;
2152 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2154 if (layers.size() < qubitsUsed[qbit]) {
2155 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2156 layers.push_back(std::move(circ));
2160 if (op->IsConditional()) {
2161 const auto bits = op->AffectedBits();
2162 for (
const auto bit : bits)
2163 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2169 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2172 while (layers.size() <= layerIdx)
2175 layers[layerIdx]->AddOperation(op->Clone());
2177 const auto writtenBits = op->AffectedBits();
2178 if (!writtenBits.empty() && !op->IsConditional()) {
2179 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2180 for (
const auto bit : writtenBits)
2181 classicalBitLayer[bit] =
2182 std::max(classicalBitLayer[bit], writtenLevel);
2186 layers.back()->AddOperation(op->Clone());
2202 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2205 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2206 std::unordered_map<size_t, size_t> classicalBitLayer;
2214 if (op->CanAffectQuantumState()) {
2215 const auto qubits = op->AffectedQubits();
2216 size_t maxLevel = 0;
2220 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2222 if (layers.size() < qubitsUsed[qbit]) {
2223 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2224 layers.push_back(std::move(circ));
2228 if (op->IsConditional()) {
2229 const auto bits = op->AffectedBits();
2230 for (
const auto bit : bits)
2231 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2237 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2240 while (layers.size() <= layerIdx)
2243 layers[layerIdx]->AddOperation(op);
2245 const auto writtenBits = op->AffectedBits();
2246 if (!writtenBits.empty() && !op->IsConditional()) {
2247 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2248 for (
const auto bit : writtenBits)
2249 classicalBitLayer[bit] =
2250 std::max(classicalBitLayer[bit], writtenLevel);
2254 layers.back()->AddOperation(op);
2272 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2275 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2276 std::unordered_map<size_t, size_t> classicalBitLayer;
2284 if (op->CanAffectQuantumState()) {
2285 const auto qubits = op->AffectedQubits();
2286 size_t maxLevel = 0;
2289 if (qubits.size() > 1) ++qubitsUsed[qbit];
2290 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2292 if (layers.size() < qubitsUsed[qbit]) {
2293 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2294 layers.push_back(std::move(circ));
2298 if (op->IsConditional()) {
2299 const auto bits = op->AffectedBits();
2300 for (
const auto bit : bits)
2301 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2307 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2310 while (layers.size() <= layerIdx)
2313 layers[layerIdx]->AddOperation(op->Clone());
2315 const auto writtenBits = op->AffectedBits();
2316 if (!writtenBits.empty() && !op->IsConditional()) {
2317 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2318 for (
const auto bit : writtenBits)
2319 classicalBitLayer[bit] =
2320 std::max(classicalBitLayer[bit], writtenLevel);
2324 layers.back()->AddOperation(op->Clone());
2340 std::vector<std::shared_ptr<Circuits::Circuit<Time>>>
2342 std::vector<std::shared_ptr<Circuits::Circuit<Time>>> layers;
2345 std::unordered_map<Types::qubit_t, Types::qubit_t> qubitsUsed;
2350 std::unordered_map<size_t, size_t> classicalBitLayer;
2358 if (op->CanAffectQuantumState()) {
2359 const auto qubits = op->AffectedQubits();
2360 size_t maxLevel = 0;
2363 if (qubits.size() > 1) ++qubitsUsed[qbit];
2364 maxLevel = std::max(maxLevel,
static_cast<size_t>(qubitsUsed[qbit]));
2366 if (layers.size() < qubitsUsed[qbit]) {
2367 auto circ = std::make_shared<Circuits::Circuit<Time>>();
2368 layers.push_back(std::move(circ));
2374 if (op->IsConditional()) {
2375 const auto bits = op->AffectedBits();
2376 for (
const auto bit : bits)
2377 maxLevel = std::max(maxLevel, classicalBitLayer[bit]);
2383 const size_t layerIdx = maxLevel > 0 ? maxLevel - 1 : 0;
2386 while (layers.size() <= layerIdx)
2389 layers[layerIdx]->AddOperation(op);
2393 const auto writtenBits = op->AffectedBits();
2394 if (!writtenBits.empty() && !op->IsConditional()) {
2395 const size_t writtenLevel = maxLevel > 0 ? maxLevel : 1;
2396 for (
const auto bit : writtenBits)
2397 classicalBitLayer[bit] =
2398 std::max(classicalBitLayer[bit], writtenLevel);
2402 layers.back()->AddOperation(op);
2419 auto circuit{std::make_shared<Circuits::Circuit<Time>>()};
2421 for (
const auto &layer : layers)
2422 circuit->AddOperations(layer->GetOperations());
2437 if (op->IsBranching())
return true;
2497 return operations.crbegin();
2514 auto size()
const {
return operations.size(); }
2522 auto empty()
const {
return operations.empty(); }
2540 const auto &
operator[](
size_t pos)
const {
return operations[pos]; }
2550 if (
size < operations.size()) operations.resize(
size);
2564 void ReplaceThreeQubitAndSwapGates(
bool onlyThreeQubits =
false) {
2573 std::vector<std::shared_ptr<IOperation<Time>>> newops;
2574 newops.reserve(operations.size());
2578 std::shared_ptr<IQuantumGate<Time>> gate =
2579 std::static_pointer_cast<IQuantumGate<Time>>(op);
2581 if (NeedsConversion(gate, onlyThreeQubits)) {
2582 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2583 ConvertGate(gate, onlyThreeQubits);
2584 newops.insert(newops.end(), newgates.begin(), newgates.end());
2586 newops.push_back(op);
2588 std::shared_ptr<ConditionalGate<Time>> condgate =
2589 std::static_pointer_cast<ConditionalGate<Time>>(op);
2590 std::shared_ptr<IQuantumGate<Time>> gate =
2591 std::static_pointer_cast<IQuantumGate<Time>>(
2592 condgate->GetOperation());
2594 if (NeedsConversion(gate, onlyThreeQubits)) {
2595 std::vector<std::shared_ptr<IGateOperation<Time>>> newgates =
2596 ConvertGate(gate, onlyThreeQubits);
2597 std::shared_ptr<ICondition> cond = condgate->GetCondition();
2599 for (
auto gate : newgates)
2601 std::make_shared<ConditionalGate<Time>>(gate, cond));
2603 newops.push_back(op);
2605 newops.push_back(op);
2608 operations.swap(newops);
2620 static bool NeedsConversion(
const std::shared_ptr<IQuantumGate<Time>> &gate,
2621 bool onlyThreeQubits =
false) {
2622 const bool hasThreeQubits = gate->GetNumQubits() == 3;
2623 if (onlyThreeQubits)
return hasThreeQubits;
2625 return hasThreeQubits ||
2641 static std::vector<std::shared_ptr<IGateOperation<Time>>> ConvertGate(
2642 std::shared_ptr<IQuantumGate<Time>> &gate,
bool onlyThreeQubits =
false) {
2645 std::vector<std::shared_ptr<IGateOperation<Time>>> newops;
2647 if (gate->GetNumQubits() == 3) {
2650 const size_t q1 = gate->GetQubit(0);
2651 const size_t q2 = gate->GetQubit(1);
2652 const size_t q3 = gate->GetQubit(2);
2655 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2656 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2657 newops.push_back(std::make_shared<CSxDagGate<Time>>(q2, q3));
2658 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2659 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2661 const size_t q1 = gate->GetQubit(0);
2662 const size_t q2 = gate->GetQubit(1);
2663 const size_t q3 = gate->GetQubit(2);
2667 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2669 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2670 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2671 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2673 newops.push_back(std::make_shared<PhaseGate<Time>>(q2, -M_PI_2));
2675 newops.push_back(std::make_shared<CSxGate<Time>>(q2, q3));
2676 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2677 newops.push_back(std::make_shared<PhaseGate<Time>>(q3, M_PI));
2679 newops.push_back(std::make_shared<CSxGate<Time>>(q1, q3));
2681 newops.push_back(std::make_shared<CXGate<Time>>(q3, q2));
2683 newops.push_back(gate);
2684 }
else if (!onlyThreeQubits &&
2687 const size_t q1 = gate->GetQubit(0);
2688 const size_t q2 = gate->GetQubit(1);
2692 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2693 newops.push_back(std::make_shared<CXGate<Time>>(q2, q1));
2694 newops.push_back(std::make_shared<CXGate<Time>>(q1, q2));
2696 newops.push_back(gate);
2717template <
typename Time = Types::time_type>
2725 std::vector<OperationPtr>;
2785 if (approximateParamsCheck) {
2786 const auto params1 = std::static_pointer_cast<IQuantumGate<Time>>(
2789 const auto params2 = std::static_pointer_cast<IQuantumGate<Time>>(
2792 if (params1.size() != params2.size())
return false;
2794 for (
size_t j = 0; j < params1.size(); ++j)
2795 if (std::abs(params1[j] - params2[j]) > paramsEpsilon)
2821 const auto leftCondition =
2822 std::static_pointer_cast<IConditionalOperation<Time>>(
2825 const auto rightCondition =
2826 std::static_pointer_cast<IConditionalOperation<Time>>(
2829 if (leftCondition->GetBitsIndices() !=
2830 rightCondition->GetBitsIndices())
2833 const auto leftEqCondition =
2834 std::static_pointer_cast<EqualCondition>(leftCondition);
2835 const auto rightEqCondition =
2836 std::static_pointer_cast<EqualCondition>(rightCondition);
2837 if (!leftEqCondition || !rightEqCondition)
return false;
2839 if (leftEqCondition->GetAllBits() != rightEqCondition->GetAllBits())
2844 std::static_pointer_cast<IConditionalOperation<Time>>(
2847 const auto rightOp =
2848 std::static_pointer_cast<IConditionalOperation<Time>>(
2858 if (leftCircuit != rightCircuit)
return false;
2929 bool approximateParamsCheck =
2931 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.
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.
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
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.
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.
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.
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.
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.
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.
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 ...
@ 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.