From c845d371b54ee420bd50be5ed3498771af605ae1 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:02:08 +0100 Subject: [PATCH 01/30] =?UTF-8?q?=F0=9F=94=A5=20Remove=20assert=20in=20cou?= =?UTF-8?q?ntGates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Mapper.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mapper.cpp b/src/Mapper.cpp index ff1377755..a9f3b3026 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -290,7 +290,6 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it, ++info.singleQubitGates; ++info.gates; } else { - assert(g->getType() == qc::X); ++info.cnots; ++info.gates; } From d52639849f32ad9690960d4bbeee7e07e8a67815 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:13:17 +0100 Subject: [PATCH 02/30] =?UTF-8?q?=E2=9C=A8=20Preserve=20operation=20type?= =?UTF-8?q?=20for=20two=20qubit=20gates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/exact/ExactMapper.cpp | 14 ++++++++++---- src/heuristic/HeuristicMapper.cpp | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 126c5bb90..bd279cfd6 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -295,8 +295,11 @@ void ExactMapper::map(const Configuration& settings) { } qcMapped.h(reverse.first); qcMapped.h(reverse.second); - qcMapped.x(reverse.second, - qc::Control{static_cast(reverse.first)}); + qcMapped.emplace_back( + qcMapped.getNqubits(), + qc::Control{static_cast(reverse.first)}, + reverse.second, op->getType(), op->getParameter().at(0), + op->getParameter().at(1), op->getParameter().at(2)); qcMapped.h(reverse.second); qcMapped.h(reverse.first); } else { @@ -305,8 +308,11 @@ void ExactMapper::map(const Configuration& settings) { << ": Added cnot with control and target: " << cnot.first << " " << cnot.second << std::endl; } - qcMapped.x(cnot.second, - qc::Control{static_cast(cnot.first)}); + qcMapped.emplace_back( + qcMapped.getNqubits(), + qc::Control{static_cast(cnot.first)}, cnot.second, + op->getType(), op->getParameter().at(0), op->getParameter().at(1), + op->getParameter().at(2)); } } } diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index 4a107a577..2b93f2214 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -108,16 +108,22 @@ void HeuristicMapper::map(const Configuration& configuration) { } qcMapped.h(reverse.first); qcMapped.h(reverse.second); - qcMapped.x(reverse.second, - qc::Control{static_cast(reverse.first)}); + qcMapped.emplace_back( + qcMapped.getNqubits(), + qc::Control{static_cast(reverse.first)}, + reverse.second, op->getType(), op->getParameter().at(0), + op->getParameter().at(1), op->getParameter().at(2)); qcMapped.h(reverse.second); qcMapped.h(reverse.first); results.output.directionReverse++; gateidx += 5; } else { - qcMapped.x(cnot.second, - qc::Control{static_cast(cnot.first)}); + qcMapped.emplace_back( + qcMapped.getNqubits(), + qc::Control{static_cast(cnot.first)}, cnot.second, + op->getType(), op->getParameter().at(0), op->getParameter().at(1), + op->getParameter().at(2)); gateidx++; } } From 27b1b20152bbe2fce8a9e005e806df69802fbd89 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:30:15 +0100 Subject: [PATCH 03/30] =?UTF-8?q?=F0=9F=9A=A7=20Generalize=20naming=20for?= =?UTF-8?q?=20two-qubit=20gates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/exact/ExactMapper.cpp | 35 ++++++++++++++++--------------- src/heuristic/HeuristicMapper.cpp | 18 ++++++++-------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index bd279cfd6..ac10dd227 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -275,23 +275,24 @@ void ExactMapper::map(const Configuration& settings) { op->getParameter().at(0), op->getParameter().at(1), op->getParameter().at(2)); } else { - const Edge cnot = {locations.at(static_cast(gate.control)), - locations.at(gate.target)}; + const Edge controlledGate = { + locations.at(static_cast(gate.control)), + locations.at(gate.target)}; - if (architecture.getCouplingMap().find(cnot) == + if (architecture.getCouplingMap().find(controlledGate) == architecture.getCouplingMap().end()) { - const Edge reverse = {cnot.second, cnot.first}; + const Edge reverse = {controlledGate.second, controlledGate.first}; if (architecture.getCouplingMap().find(reverse) == architecture.getCouplingMap().end()) { - throw QMAPException( - "Invalid CNOT: " + std::to_string(reverse.first) + "-" + - std::to_string(reverse.second)); + throw QMAPException("Invalid controlled gate " + op->getName() + + ": " + std::to_string(reverse.first) + "-" + + std::to_string(reverse.second)); } if (settings.verbose) { - std::cout - << i - << ": Added (direction-reversed) cnot with control and target: " - << cnot.first << " " << cnot.second << std::endl; + std::cout << i << ": Added (direction-reversed) controlled gate " + << op->getName() + << " with control and target: " << controlledGate.first + << " " << controlledGate.second << std::endl; } qcMapped.h(reverse.first); qcMapped.h(reverse.second); @@ -304,15 +305,15 @@ void ExactMapper::map(const Configuration& settings) { qcMapped.h(reverse.first); } else { if (settings.verbose) { - std::cout << i - << ": Added cnot with control and target: " << cnot.first - << " " << cnot.second << std::endl; + std::cout << i << ": Added controlled gate " << op->getName() + << " with control and target: " << controlledGate.first + << " " << controlledGate.second << std::endl; } qcMapped.emplace_back( qcMapped.getNqubits(), - qc::Control{static_cast(cnot.first)}, cnot.second, - op->getType(), op->getParameter().at(0), op->getParameter().at(1), - op->getParameter().at(2)); + qc::Control{static_cast(controlledGate.first)}, + controlledGate.second, op->getType(), op->getParameter().at(0), + op->getParameter().at(1), op->getParameter().at(2)); } } } diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index 2b93f2214..367663656 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -94,17 +94,17 @@ void HeuristicMapper::map(const Configuration& configuration) { gateidx++; } } else { - const Edge cnot = { + const Edge controlledGate = { locations.at(static_cast(gate.control)), locations.at(gate.target)}; - if (architecture.getCouplingMap().find(cnot) == + if (architecture.getCouplingMap().find(controlledGate) == architecture.getCouplingMap().end()) { - const Edge reverse = {cnot.second, cnot.first}; + const Edge reverse = {controlledGate.second, controlledGate.first}; if (architecture.getCouplingMap().find(reverse) == architecture.getCouplingMap().end()) { - throw QMAPException( - "Invalid CNOT: " + std::to_string(reverse.first) + "-" + - std::to_string(reverse.second)); + throw QMAPException("Invalid controlled gate " + op->getName() + + ": " + std::to_string(reverse.first) + "-" + + std::to_string(reverse.second)); } qcMapped.h(reverse.first); qcMapped.h(reverse.second); @@ -121,9 +121,9 @@ void HeuristicMapper::map(const Configuration& configuration) { } else { qcMapped.emplace_back( qcMapped.getNqubits(), - qc::Control{static_cast(cnot.first)}, cnot.second, - op->getType(), op->getParameter().at(0), op->getParameter().at(1), - op->getParameter().at(2)); + qc::Control{static_cast(controlledGate.first)}, + controlledGate.second, op->getType(), op->getParameter().at(0), + op->getParameter().at(1), op->getParameter().at(2)); gateidx++; } } From 7002c939c954ee42f41ab60b90bfb4d4d1dd9a73 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:37:31 +0100 Subject: [PATCH 04/30] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20cnots=20to=20twoQ?= =?UTF-8?q?ubitGates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/MappingResults.hpp | 10 +++++----- mqt/qmap/bindings.cpp | 3 ++- src/Mapper.cpp | 6 +++--- src/exact/ExactMapper.cpp | 8 ++++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/MappingResults.hpp b/include/MappingResults.hpp index c869f2461..a357f800f 100644 --- a/include/MappingResults.hpp +++ b/include/MappingResults.hpp @@ -19,7 +19,7 @@ struct MappingResults { std::uint16_t qubits = 0; std::size_t gates = 0; std::size_t singleQubitGates = 0; - std::size_t cnots = 0; + std::size_t twoQubitGates = 0; std::size_t layers = 0; // info in output circuit @@ -62,14 +62,14 @@ struct MappingResults { circuit["qubits"] = input.qubits; circuit["gates"] = input.gates; circuit["single_qubit_gates"] = input.singleQubitGates; - circuit["cnots"] = input.cnots; + circuit["cnots"] = input.twoQubitGates; auto& mappedCirc = resultJSON["mapped_circuit"]; mappedCirc["name"] = output.name; mappedCirc["qubits"] = output.qubits; mappedCirc["gates"] = output.gates; mappedCirc["single_qubit_gates"] = output.singleQubitGates; - mappedCirc["cnots"] = output.cnots; + mappedCirc["cnots"] = output.twoQubitGates; if (!mappedCircuit.empty()) { mappedCirc["qasm"] = mappedCircuit; } @@ -100,9 +100,9 @@ struct MappingResults { virtual std::string csv() { std::stringstream ss{}; ss << input.name << ";" << input.qubits << ";" << input.gates << ";" - << input.singleQubitGates << ";" << input.cnots << ";" << architecture + << input.singleQubitGates << ";" << input.twoQubitGates << ";" << architecture << ";" << output.name << ";" << output.qubits << ";" << output.gates - << ";" << output.singleQubitGates << ";" << output.cnots << ";" + << ";" << output.singleQubitGates << ";" << output.twoQubitGates << ";" << output.swaps << ";" << output.directionReverse << ";" << output.teleportations << ";"; if (timeout) { diff --git a/mqt/qmap/bindings.cpp b/mqt/qmap/bindings.cpp index 6382027a2..2aa4b1bba 100644 --- a/mqt/qmap/bindings.cpp +++ b/mqt/qmap/bindings.cpp @@ -240,7 +240,8 @@ PYBIND11_MODULE(pyqmap, m) { .def_readwrite("gates", &MappingResults::CircuitInfo::gates) .def_readwrite("single_qubit_gates", &MappingResults::CircuitInfo::singleQubitGates) - .def_readwrite("cnots", &MappingResults::CircuitInfo::cnots) + .def_readwrite("two_qubit_gates", + &MappingResults::CircuitInfo::twoQubitGates) .def_readwrite("layers", &MappingResults::CircuitInfo::layers) .def_readwrite("swaps", &MappingResults::CircuitInfo::swaps) .def_readwrite("direction_reverse", diff --git a/src/Mapper.cpp b/src/Mapper.cpp index a9f3b3026..5e0d23f3e 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -280,17 +280,17 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it, if (g->getType() == qc::SWAP) { if (architecture.bidirectional()) { info.gates += GATES_OF_BIDIRECTIONAL_SWAP; - info.cnots += GATES_OF_BIDIRECTIONAL_SWAP; + info.twoQubitGates += GATES_OF_BIDIRECTIONAL_SWAP; } else { info.gates += GATES_OF_UNIDIRECTIONAL_SWAP; - info.cnots += GATES_OF_BIDIRECTIONAL_SWAP; + info.twoQubitGates += GATES_OF_BIDIRECTIONAL_SWAP; info.singleQubitGates += GATES_OF_DIRECTION_REVERSE; } } else if (g->getControls().empty()) { ++info.singleQubitGates; ++info.gates; } else { - ++info.cnots; + ++info.twoQubitGates; ++info.gates; } continue; diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index ac10dd227..18f756e14 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -361,7 +361,7 @@ void ExactMapper::map(const Configuration& settings) { // 10) re-count gates results.output.singleQubitGates = 0U; - results.output.cnots = 0U; + results.output.twoQubitGates = 0U; results.output.gates = 0U; countGates(qcMapped, results.output); @@ -769,9 +769,9 @@ number of variables: (|L|-1) * m! // quickly determine cost choiceResults.output.singleQubitGates = choiceResults.input.singleQubitGates; - choiceResults.output.cnots = choiceResults.input.cnots; - choiceResults.output.gates = - choiceResults.output.singleQubitGates + choiceResults.output.cnots; + choiceResults.output.twoQubitGates = choiceResults.input.twoQubitGates; + choiceResults.output.gates = choiceResults.output.singleQubitGates + + choiceResults.output.twoQubitGates; assert(choiceResults.output.swaps == 0U); assert(choiceResults.output.directionReverse == 0U); // swaps From 03f4214441f4b4321a1a84545db5a95c1cea4153 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:03:01 +0000 Subject: [PATCH 05/30] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/MappingResults.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/MappingResults.hpp b/include/MappingResults.hpp index a357f800f..24e87d849 100644 --- a/include/MappingResults.hpp +++ b/include/MappingResults.hpp @@ -100,11 +100,11 @@ struct MappingResults { virtual std::string csv() { std::stringstream ss{}; ss << input.name << ";" << input.qubits << ";" << input.gates << ";" - << input.singleQubitGates << ";" << input.twoQubitGates << ";" << architecture - << ";" << output.name << ";" << output.qubits << ";" << output.gates - << ";" << output.singleQubitGates << ";" << output.twoQubitGates << ";" - << output.swaps << ";" << output.directionReverse << ";" - << output.teleportations << ";"; + << input.singleQubitGates << ";" << input.twoQubitGates << ";" + << architecture << ";" << output.name << ";" << output.qubits << ";" + << output.gates << ";" << output.singleQubitGates << ";" + << output.twoQubitGates << ";" << output.swaps << ";" + << output.directionReverse << ";" << output.teleportations << ";"; if (timeout) { ss << "TO"; } else { From 5f4902365a9fe6136aff1438251dd7842ae3fc80 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 20 Feb 2023 10:39:13 +0100 Subject: [PATCH 06/30] :sparkles: Case distinction for flipping gates --- include/Mapper.hpp | 24 ++++++++++++++++-- src/Mapper.cpp | 41 +++++++++++++++++++++++++++++++ src/exact/ExactMapper.cpp | 16 ++---------- src/heuristic/HeuristicMapper.cpp | 19 ++------------ 4 files changed, 67 insertions(+), 33 deletions(-) diff --git a/include/Mapper.hpp b/include/Mapper.hpp index 2ce39d582..92513fbec 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -152,13 +152,33 @@ class Mapper { countGates(circuit.cbegin(), circuit.cend(), info); } /** - * @brief count number of elementary gates and cnots in circuit and save the - * results in `info.gates` and `info.cnots` + * @brief count number of elementary gates and controlled gates in circuit and + * save the results in `info.gates` and `info.twoQubitGates` */ virtual void countGates(decltype(qcMapped.cbegin()) it, const decltype(qcMapped.cend())& end, MappingResults::CircuitInfo& info); + /** + * @brief inserts a two qubit operation op along edge. + * @param edge the edge in the coupling graph that controls on which physical + * qubits the operation will act. The logical and physical direction are the + * same. + * @param op the operation which is inserted + * @return total number of inserted operations + */ + std::size_t insertGate(const Edge& edge, qc::StandardOperation* op); + + /** + * @brief inserts a two qubit operation op along a direction reversed edge. + * @param edge the edge in the coupling graph that controls on which physical + * qubits the operation will act. The logical direction of the operation is + * the reverse of the physical direction. + * @param op the operation which is inserted + * @return total number of inserted operations + */ + std::size_t insertFlippedGate(const Edge& edge, qc::StandardOperation* op); + /** * @brief performs optimizations on the circuit before mapping * diff --git a/src/Mapper.cpp b/src/Mapper.cpp index 5e0d23f3e..9b3a07626 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -302,3 +302,44 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it, } } } +std::size_t Mapper::insertGate(const Edge& edge, qc::StandardOperation* op) { + qcMapped.emplace_back( + qcMapped.getNqubits(), qc::Control{static_cast(edge.first)}, + edge.second, op->getType(), op->getParameter().at(0), + op->getParameter().at(1), op->getParameter().at(2)); + return 1; +} + +std::size_t Mapper::insertFlippedGate(const Edge& edge, qc::StandardOperation* op) { + int swapType = -1; + switch (op->getType()) { + case qc::X: + swapType = 1; + break; + case qc::Z: + swapType = 0; + break; + default: + swapType = -1; + } + + if(swapType == 0) { + // no additional gates required to reverse direction + return insertGate(edge, op); + } + if(swapType == 1) { + // four hadamard gates to reverse direction + qcMapped.h(edge.first); + qcMapped.h(edge.second); + auto tmp = insertGate(edge, op); + qcMapped.h(edge.first); + qcMapped.h(edge.second); + return tmp + 4; + } + + // in all other cases, swap gates are used to reverse direction + qcMapped.swap(edge.first, edge.second); + auto tmp = insertGate(edge, op); + qcMapped.swap(edge.second, edge.first); + return 2 + tmp; +} diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 18f756e14..61c1a66fe 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -294,26 +294,14 @@ void ExactMapper::map(const Configuration& settings) { << " with control and target: " << controlledGate.first << " " << controlledGate.second << std::endl; } - qcMapped.h(reverse.first); - qcMapped.h(reverse.second); - qcMapped.emplace_back( - qcMapped.getNqubits(), - qc::Control{static_cast(reverse.first)}, - reverse.second, op->getType(), op->getParameter().at(0), - op->getParameter().at(1), op->getParameter().at(2)); - qcMapped.h(reverse.second); - qcMapped.h(reverse.first); + insertFlippedGate(reverse, op); } else { if (settings.verbose) { std::cout << i << ": Added controlled gate " << op->getName() << " with control and target: " << controlledGate.first << " " << controlledGate.second << std::endl; } - qcMapped.emplace_back( - qcMapped.getNqubits(), - qc::Control{static_cast(controlledGate.first)}, - controlledGate.second, op->getType(), op->getParameter().at(0), - op->getParameter().at(1), op->getParameter().at(2)); + insertGate(controlledGate, op); } } } diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index 367663656..0214253ed 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -106,25 +106,10 @@ void HeuristicMapper::map(const Configuration& configuration) { ": " + std::to_string(reverse.first) + "-" + std::to_string(reverse.second)); } - qcMapped.h(reverse.first); - qcMapped.h(reverse.second); - qcMapped.emplace_back( - qcMapped.getNqubits(), - qc::Control{static_cast(reverse.first)}, - reverse.second, op->getType(), op->getParameter().at(0), - op->getParameter().at(1), op->getParameter().at(2)); - qcMapped.h(reverse.second); - qcMapped.h(reverse.first); - + gateidx += insertFlippedGate(reverse, op); results.output.directionReverse++; - gateidx += 5; } else { - qcMapped.emplace_back( - qcMapped.getNqubits(), - qc::Control{static_cast(controlledGate.first)}, - controlledGate.second, op->getType(), op->getParameter().at(0), - op->getParameter().at(1), op->getParameter().at(2)); - gateidx++; + gateidx += insertGate(controlledGate, op); } } } From 4e8648ab44794b1756fab0aa54cbcda2dae94928 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 09:39:47 +0000 Subject: [PATCH 07/30] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Mapper.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Mapper.cpp b/src/Mapper.cpp index 9b3a07626..7699f20f5 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -310,7 +310,8 @@ std::size_t Mapper::insertGate(const Edge& edge, qc::StandardOperation* op) { return 1; } -std::size_t Mapper::insertFlippedGate(const Edge& edge, qc::StandardOperation* op) { +std::size_t Mapper::insertFlippedGate(const Edge& edge, + qc::StandardOperation* op) { int swapType = -1; switch (op->getType()) { case qc::X: @@ -323,11 +324,11 @@ std::size_t Mapper::insertFlippedGate(const Edge& edge, qc::StandardOperation* o swapType = -1; } - if(swapType == 0) { + if (swapType == 0) { // no additional gates required to reverse direction return insertGate(edge, op); } - if(swapType == 1) { + if (swapType == 1) { // four hadamard gates to reverse direction qcMapped.h(edge.first); qcMapped.h(edge.second); From 6d33c877d2e86fd1960d94b95ccc5a1232b5a7a6 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Sat, 4 Mar 2023 13:23:45 +0100 Subject: [PATCH 08/30] =?UTF-8?q?=E2=9C=A8=20Function=20for=20gate=20rever?= =?UTF-8?q?sal=20cost?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Architecture.hpp | 9 ++++- include/Mapper.hpp | 4 +-- include/heuristic/HeuristicMapper.hpp | 2 +- src/Architecture.cpp | 45 ++++++++++++++++++++++++ src/Mapper.cpp | 50 +++++++++++---------------- src/exact/ExactMapper.cpp | 17 +++++---- src/heuristic/HeuristicMapper.cpp | 4 +-- 7 files changed, 89 insertions(+), 42 deletions(-) diff --git a/include/Architecture.hpp b/include/Architecture.hpp index 2487063fd..82bb407ac 100644 --- a/include/Architecture.hpp +++ b/include/Architecture.hpp @@ -32,6 +32,8 @@ constexpr std::uint32_t COST_TELEPORTATION = 2 * COST_CNOT_GATE + COST_MEASUREMENT + 4 * COST_SINGLE_QUBIT_GATE; constexpr std::uint32_t COST_DIRECTION_REVERSE = 4 * COST_SINGLE_QUBIT_GATE; +enum class GateFlipStrategy { Unknown, Identity, Swap, Hadamard }; + class Architecture { public: class Properties { @@ -348,6 +350,10 @@ class Architecture { static void printCouplingMap(const CouplingMap& cm, std::ostream& os); + static GateFlipStrategy getGateFlipStrategy(qc::OpType opType); + static std::uint32_t computeCostDirectionReverse(qc::OpType opType); + static std::uint32_t computeGatesDirectionReverse(qc::OpType opType); + protected: std::string name; std::uint16_t nqubits = 0; @@ -377,7 +383,8 @@ class Architecture { if (node.containsCorrectEdge) { return length * COST_UNIDIRECTIONAL_SWAP; } - return length * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE; + return length * COST_UNIDIRECTIONAL_SWAP + + Architecture::computeCostDirectionReverse(qc::OpType::None); } // added for teleportation diff --git a/include/Mapper.hpp b/include/Mapper.hpp index 92513fbec..621d277d5 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -167,7 +167,7 @@ class Mapper { * @param op the operation which is inserted * @return total number of inserted operations */ - std::size_t insertGate(const Edge& edge, qc::StandardOperation* op); + std::size_t insertGate(const Edge& edge, const qc::Operation& op); /** * @brief inserts a two qubit operation op along a direction reversed edge. @@ -177,7 +177,7 @@ class Mapper { * @param op the operation which is inserted * @return total number of inserted operations */ - std::size_t insertFlippedGate(const Edge& edge, qc::StandardOperation* op); + std::size_t insertFlippedGate(const Edge& edge, const qc::Operation& op); /** * @brief performs optimizations on the circuit before mapping diff --git a/include/heuristic/HeuristicMapper.hpp b/include/heuristic/HeuristicMapper.hpp index 32c4132c9..54767f8a7 100644 --- a/include/heuristic/HeuristicMapper.hpp +++ b/include/heuristic/HeuristicMapper.hpp @@ -189,7 +189,7 @@ class HeuristicMapper : public Mapper { } else { costHeur += fidelityCost; } - if (cost > COST_DIRECTION_REVERSE) { + if (cost > Architecture::computeCostDirectionReverse(gate.op->getType())) { done = false; return; } diff --git a/src/Architecture.cpp b/src/Architecture.cpp index 4b352ba6d..413c351d4 100644 --- a/src/Architecture.cpp +++ b/src/Architecture.cpp @@ -687,3 +687,48 @@ void Architecture::printCouplingMap(const CouplingMap& cm, std::ostream& os) { } os << "}" << std::endl; } + +GateFlipStrategy Architecture::getGateFlipStrategy(qc::OpType opType) { + switch (opType) { + case qc::None: + return GateFlipStrategy::Unknown; + case qc::X: + case qc::SX: + case qc::SXdag: + return GateFlipStrategy::Hadamard; + case qc::Z: + case qc::S: + case qc::Sdag: + case qc::T: + case qc::Tdag: + case qc::Phase: + case qc::RZ: + case qc::SWAP: + return GateFlipStrategy::Identity; + default: + return GateFlipStrategy::Swap; + } +} + +std::uint32_t Architecture::computeCostDirectionReverse(qc::OpType opType) { + switch (Architecture::getGateFlipStrategy(opType)) { + case GateFlipStrategy::Identity: + return 0; + case GateFlipStrategy::Hadamard: + return 4 * COST_SINGLE_QUBIT_GATE; + case GateFlipStrategy::Swap: + case GateFlipStrategy::Unknown: + return 2 * COST_BIDIRECTIONAL_SWAP; + } +} +std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) { + switch (Architecture::getGateFlipStrategy(opType)) { + case GateFlipStrategy::Identity: + return 0; + case GateFlipStrategy::Hadamard: + return 4; + case GateFlipStrategy::Swap: + case GateFlipStrategy::Unknown: + return 2 * GATES_OF_BIDIRECTIONAL_SWAP; + } +} diff --git a/src/Mapper.cpp b/src/Mapper.cpp index 7699f20f5..6b9b994bd 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -284,7 +284,7 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it, } else { info.gates += GATES_OF_UNIDIRECTIONAL_SWAP; info.twoQubitGates += GATES_OF_BIDIRECTIONAL_SWAP; - info.singleQubitGates += GATES_OF_DIRECTION_REVERSE; + info.singleQubitGates += Architecture::computeGatesDirectionReverse(g->getType()); } } else if (g->getControls().empty()) { ++info.singleQubitGates; @@ -302,45 +302,35 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it, } } } -std::size_t Mapper::insertGate(const Edge& edge, qc::StandardOperation* op) { - qcMapped.emplace_back( - qcMapped.getNqubits(), qc::Control{static_cast(edge.first)}, - edge.second, op->getType(), op->getParameter().at(0), - op->getParameter().at(1), op->getParameter().at(2)); +std::size_t Mapper::insertGate(const Edge& edge, const qc::Operation& op) { + auto newOp = op.clone(); + newOp->setControls({qc::Control{edge.first}}); + newOp->setTargets({edge.second}); + qcMapped.emplace_back(newOp); return 1; } -std::size_t Mapper::insertFlippedGate(const Edge& edge, - qc::StandardOperation* op) { - int swapType = -1; - switch (op->getType()) { - case qc::X: - swapType = 1; - break; - case qc::Z: - swapType = 0; - break; - default: - swapType = -1; - } - - if (swapType == 0) { +std::size_t Mapper::insertFlippedGate(const Edge& edge, + const qc::Operation& op) { + std::size_t tmp = 0; + switch (Architecture::getGateFlipStrategy(op.getType())) { + case GateFlipStrategy::Identity: // no additional gates required to reverse direction return insertGate(edge, op); - } - if (swapType == 1) { + case GateFlipStrategy::Swap: + case GateFlipStrategy::Unknown: + // swap gates are used to reverse direction + qcMapped.swap(edge.first, edge.second); + tmp = insertGate(edge, op); + qcMapped.swap(edge.second, edge.first); + return 2 + tmp; + case GateFlipStrategy::Hadamard: // four hadamard gates to reverse direction qcMapped.h(edge.first); qcMapped.h(edge.second); - auto tmp = insertGate(edge, op); + tmp = insertGate(edge, op); qcMapped.h(edge.first); qcMapped.h(edge.second); return tmp + 4; } - - // in all other cases, swap gates are used to reverse direction - qcMapped.swap(edge.first, edge.second); - auto tmp = insertGate(edge, op); - qcMapped.swap(edge.second, edge.first); - return 2 + tmp; } diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 61c1a66fe..e89a466ff 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -294,14 +294,14 @@ void ExactMapper::map(const Configuration& settings) { << " with control and target: " << controlledGate.first << " " << controlledGate.second << std::endl; } - insertFlippedGate(reverse, op); + insertFlippedGate(reverse, *op); } else { if (settings.verbose) { std::cout << i << ": Added controlled gate " << op->getName() << " with control and target: " << controlledGate.first << " " << controlledGate.second << std::endl; } - insertGate(controlledGate, op); + insertGate(controlledGate, *op); } } } @@ -733,9 +733,12 @@ number of variables: (|L|-1) * m! [static_cast(gate.control)]; reverse = reverse && (!indexFT || !indexSC); } - cost = cost + LogicTerm::ite(reverse, - LogicTerm(GATES_OF_DIRECTION_REVERSE), - LogicTerm(0)); + cost = cost + + LogicTerm::ite(reverse, + LogicTerm(static_cast( + Architecture::computeGatesDirectionReverse( + gate.op->getType()))), + LogicTerm(0)); } } lb->minimize(cost); @@ -836,7 +839,9 @@ number of variables: (|L|-1) * m! if (m->getBoolValue(indexFT, lb.get()) && m->getBoolValue(indexSC, lb.get())) { choiceResults.output.directionReverse++; - choiceResults.output.gates += GATES_OF_DIRECTION_REVERSE; + choiceResults.output.gates += + Architecture::computeGatesDirectionReverse( + gate.op->getType()); } } } diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index 0214253ed..a64653c10 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -106,10 +106,10 @@ void HeuristicMapper::map(const Configuration& configuration) { ": " + std::to_string(reverse.first) + "-" + std::to_string(reverse.second)); } - gateidx += insertFlippedGate(reverse, op); + gateidx += insertFlippedGate(reverse, *op); results.output.directionReverse++; } else { - gateidx += insertGate(controlledGate, op); + gateidx += insertGate(controlledGate, *op); } } } From 3b05b62d98f0947c677d3204f2d42c6f7ea335ea Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Sun, 5 Mar 2023 13:26:24 +0100 Subject: [PATCH 09/30] :rewind: Assume cnot gates in distance table --- include/Architecture.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/Architecture.hpp b/include/Architecture.hpp index 82bb407ac..c4d9420bb 100644 --- a/include/Architecture.hpp +++ b/include/Architecture.hpp @@ -383,8 +383,9 @@ class Architecture { if (node.containsCorrectEdge) { return length * COST_UNIDIRECTIONAL_SWAP; } + // TODO: distance has no gate context, but gates have different costs return length * COST_UNIDIRECTIONAL_SWAP + - Architecture::computeCostDirectionReverse(qc::OpType::None); + Architecture::computeCostDirectionReverse(qc::OpType::X); } // added for teleportation From 7b3808549039b1548189af1b35921929adfb44a6 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Sun, 5 Mar 2023 13:40:12 +0100 Subject: [PATCH 10/30] :white_check_mark: Add direction reverse python tests --- test/python/test_exact_mapper.py | 130 +++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index c9e90d19e..af9ab37ba 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -1,7 +1,11 @@ +from typing import Callable + +import pytest from mqt import qmap from mqt.qcec import verify from qiskit import QuantumCircuit +from qiskit.circuit import InstructionSet from qiskit.providers.fake_provider import FakeLondon @@ -58,3 +62,129 @@ def test_exact_non_trivial_swaps() -> None: result = verify(qc, qc_mapped) assert result.considered_equivalent() is True + + +@pytest.fixture +def one_way_arch() -> qmap.Architecture: + arch = qmap.Architecture( + 2, + { + (0, 1), + }, + ) + return arch + + +@pytest.mark.parametrize( + "gate", + [ + QuantumCircuit.cx, + QuantumCircuit.csx, + ], +) +def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + qc = QuantumCircuit(2) + gate(qc, 0, 1) + gate(qc, 1, 0) + qc.measure_all() + + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + assert qc_mapped.count_ops()["h"] == 4 + assert "swap" not in qc_mapped.count_ops() + + +@pytest.mark.parametrize( + "gate", + [ + QuantumCircuit.cy, + lambda qc, ctrl, target: QuantumCircuit.crx(qc, 0.5, ctrl, target), + lambda qc, ctrl, target: QuantumCircuit.cry(qc, 0.5, ctrl, target), + ], +) +def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + qc = QuantumCircuit(2) + gate(qc, 0, 1) + gate(qc, 1, 0) + qc.measure_all() + + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + assert "h" not in qc_mapped.count_ops() + assert qc_mapped.count_ops()["swap"] == 2 + + +@pytest.mark.parametrize( + "gate", + [ + QuantumCircuit.cz, + lambda qc, ctrl, target: QuantumCircuit.cp(qc, 0.5, ctrl, target), + lambda qc, ctrl, target: QuantumCircuit.crz(qc, 0.5, ctrl, target), + ], +) +def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + qc = QuantumCircuit(2) + gate(qc, 0, 1) + gate(qc, 1, 0) + qc.measure_all() + + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + assert "h" not in qc_mapped.count_ops() + assert "swap" not in qc_mapped.count_ops() + + +@pytest.mark.parametrize( + "gate", + [ + QuantumCircuit.cx, + QuantumCircuit.cy, + QuantumCircuit.cz, + QuantumCircuit.csx, + QuantumCircuit.ch, + QuantumCircuit.cnot, + QuantumCircuit.cs, + QuantumCircuit.csdg, + ], +) +def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + qc = QuantumCircuit(2) + gate(qc, 0, 1) + gate(qc, 1, 0) + qc.measure_all() + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + + result = verify(qc, qc_mapped) + assert result.considered_equivalent() is True + + +@pytest.mark.parametrize( + "gate", + [ + QuantumCircuit.cp, + QuantumCircuit.crx, + QuantumCircuit.cry, + QuantumCircuit.crz, + ], +) +def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + qc = QuantumCircuit(2) + gate(qc, 0.25, 0, 1) + gate(qc, 0.75, 1, 0) + qc.measure_all() + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + + result = verify(qc, qc_mapped) + assert result.considered_equivalent() is True + + +@pytest.mark.parametrize( + "gate", + [QuantumCircuit.cu], +) +def test_direction_reverse_params_4(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + qc = QuantumCircuit(2) + gate(qc, 0.1, 0.2, 0.3, 0.4, 0, 1) + gate(qc, 0.9, 0.8, 0.7, 0.6, 1, 0) + qc.measure_all() + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + + result = verify(qc, qc_mapped) + assert result.considered_equivalent() is True From e7127ddb4201eab7c24e8727be65fb796890afb7 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 6 Mar 2023 11:48:49 +0100 Subject: [PATCH 11/30] :bulb: Add python test descriptions --- test/python/test_exact_mapper.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index af9ab37ba..4243962b1 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -83,6 +83,7 @@ def one_way_arch() -> qmap.Architecture: ], ) def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + """Verify that control and target are flipped using four hadamard gates for some gates where this is possible.""" qc = QuantumCircuit(2) gate(qc, 0, 1) gate(qc, 1, 0) @@ -102,6 +103,7 @@ def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Calla ], ) def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + """Verify that control and target are flipped using two swap gates for some gates where this is possible.""" qc = QuantumCircuit(2) gate(qc, 0, 1) gate(qc, 1, 0) @@ -121,6 +123,7 @@ def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[ ], ) def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + """Verify that control and target are flipped without adding additional for some gates where this is possible.""" qc = QuantumCircuit(2) gate(qc, 0, 1) gate(qc, 1, 0) @@ -145,6 +148,7 @@ def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Calla ], ) def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + """Verify that control and target is flipped correctly for all two qubit controlled gates with zero parameters.""" qc = QuantumCircuit(2) gate(qc, 0, 1) gate(qc, 1, 0) @@ -165,6 +169,7 @@ def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Calla ], ) def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + """Verify that control and target is flipped correctly for all two qubit controlled gates with one parameter.""" qc = QuantumCircuit(2) gate(qc, 0.25, 0, 1) gate(qc, 0.75, 1, 0) @@ -180,6 +185,7 @@ def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Calla [QuantumCircuit.cu], ) def test_direction_reverse_params_4(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: + """Verify that control and target is flipped correctly for all two qubit controlled gates with four parameters.""" qc = QuantumCircuit(2) gate(qc, 0.1, 0.2, 0.3, 0.4, 0, 1) gate(qc, 0.9, 0.8, 0.7, 0.6, 1, 0) From 6dfbb94dfac0ad96fa7d27958ff81121765dd5fe Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 6 Mar 2023 12:11:09 +0100 Subject: [PATCH 12/30] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20ruff=20warnings=20fo?= =?UTF-8?q?r=20python=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/python/test_exact_mapper.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 4243962b1..1dc247ad4 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -1,13 +1,17 @@ -from typing import Callable +"""Test the exact mapper.""" + +from typing import TYPE_CHECKING, Callable import pytest from mqt import qmap from mqt.qcec import verify from qiskit import QuantumCircuit -from qiskit.circuit import InstructionSet from qiskit.providers.fake_provider import FakeLondon +if TYPE_CHECKING: + from qiskit.circuit import InstructionSet + def test_exact_no_swaps_trivial_layout() -> None: """Verify that the exact mapper works on a simple circuit that requires no swaps on a trivial initial layout.""" @@ -64,15 +68,15 @@ def test_exact_non_trivial_swaps() -> None: assert result.considered_equivalent() is True -@pytest.fixture +@pytest.fixture() def one_way_arch() -> qmap.Architecture: - arch = qmap.Architecture( + """Return a simple one way architecture to test direction reversal.""" + return qmap.Architecture( 2, { (0, 1), }, ) - return arch @pytest.mark.parametrize( From ff3929c918b6aa2dec16015f68257972ee96b190 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 6 Mar 2023 13:07:23 +0100 Subject: [PATCH 13/30] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20missing=20return=20o?= =?UTF-8?q?n=20unexpected=20enum=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Architecture.cpp | 4 ++++ src/Mapper.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/Architecture.cpp b/src/Architecture.cpp index 413c351d4..13b40ad92 100644 --- a/src/Architecture.cpp +++ b/src/Architecture.cpp @@ -719,6 +719,8 @@ std::uint32_t Architecture::computeCostDirectionReverse(qc::OpType opType) { case GateFlipStrategy::Swap: case GateFlipStrategy::Unknown: return 2 * COST_BIDIRECTIONAL_SWAP; + default: + throw QMAPException("Unexpected GateFlipStrategy!"); } } std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) { @@ -730,5 +732,7 @@ std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) { case GateFlipStrategy::Swap: case GateFlipStrategy::Unknown: return 2 * GATES_OF_BIDIRECTIONAL_SWAP; + default: + throw QMAPException("Unexpected GateFlipStrategy!"); } } diff --git a/src/Mapper.cpp b/src/Mapper.cpp index 1c38e1115..b8c9ab2a9 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -354,5 +354,7 @@ std::size_t Mapper::insertFlippedGate(const Edge& edge, qcMapped.h(edge.first); qcMapped.h(edge.second); return tmp + 4; + default: + throw QMAPException("Unexpected GateFlipStrategy!"); } } From 91b79691a2474aaa1f6fa89df9f8ea3f96ab3ef9 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Mon, 6 Mar 2023 13:34:54 +0100 Subject: [PATCH 14/30] =?UTF-8?q?=E2=9C=85=20Remove=20InstructionSet=20imp?= =?UTF-8?q?ort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/python/test_exact_mapper.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 609f1e7c6..241ba32af 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -1,6 +1,6 @@ """Test the exact mapper.""" -from typing import TYPE_CHECKING, Callable +from typing import Any, Callable import pytest from qiskit import QuantumCircuit @@ -9,9 +9,6 @@ from mqt import qmap from mqt.qcec import verify -if TYPE_CHECKING: - from qiskit.circuit import InstructionSet - def test_exact_no_swaps_trivial_layout() -> None: """Verify that the exact mapper works on a simple circuit that requires no swaps on a trivial initial layout.""" @@ -86,7 +83,7 @@ def one_way_arch() -> qmap.Architecture: QuantumCircuit.csx, ], ) -def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: +def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: """Verify that control and target are flipped using four hadamard gates for some gates where this is possible.""" qc = QuantumCircuit(2) gate(qc, 0, 1) @@ -106,7 +103,7 @@ def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Calla lambda qc, ctrl, target: QuantumCircuit.cry(qc, 0.5, ctrl, target), ], ) -def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: +def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: """Verify that control and target are flipped using two swap gates for some gates where this is possible.""" qc = QuantumCircuit(2) gate(qc, 0, 1) @@ -126,7 +123,7 @@ def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[ lambda qc, ctrl, target: QuantumCircuit.crz(qc, 0.5, ctrl, target), ], ) -def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: +def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: """Verify that control and target are flipped without adding additional for some gates where this is possible.""" qc = QuantumCircuit(2) gate(qc, 0, 1) @@ -151,7 +148,7 @@ def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Calla QuantumCircuit.csdg, ], ) -def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: +def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: """Verify that control and target is flipped correctly for all two qubit controlled gates with zero parameters.""" qc = QuantumCircuit(2) gate(qc, 0, 1) @@ -172,7 +169,7 @@ def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Calla QuantumCircuit.crz, ], ) -def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: +def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: """Verify that control and target is flipped correctly for all two qubit controlled gates with one parameter.""" qc = QuantumCircuit(2) gate(qc, 0.25, 0, 1) @@ -188,7 +185,7 @@ def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Calla "gate", [QuantumCircuit.cu], ) -def test_direction_reverse_params_4(one_way_arch: qmap.Architecture, gate: Callable[..., InstructionSet]) -> None: +def test_direction_reverse_params_4(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: """Verify that control and target is flipped correctly for all two qubit controlled gates with four parameters.""" qc = QuantumCircuit(2) gate(qc, 0.1, 0.2, 0.3, 0.4, 0, 1) From 219eda2e60a030a74bae85b8b6bdbf198987a4a4 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Tue, 7 Mar 2023 18:19:02 +0100 Subject: [PATCH 15/30] =?UTF-8?q?=E2=9C=85=20Use=20gate=20objects=20for=20?= =?UTF-8?q?python=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/python/test_exact_mapper.py | 106 +++++++++---------------------- 1 file changed, 31 insertions(+), 75 deletions(-) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 241ba32af..69739594f 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -1,8 +1,6 @@ """Test the exact mapper.""" - -from typing import Any, Callable - import pytest +import qiskit from qiskit import QuantumCircuit from qiskit.providers.fake_provider import FakeLondon @@ -78,16 +76,13 @@ def one_way_arch() -> qmap.Architecture: @pytest.mark.parametrize( "gate", - [ - QuantumCircuit.cx, - QuantumCircuit.csx, - ], + [qiskit.circuit.library.CXGate(), qiskit.circuit.library.CSXGate()], ) -def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: +def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: """Verify that control and target are flipped using four hadamard gates for some gates where this is possible.""" qc = QuantumCircuit(2) - gate(qc, 0, 1) - gate(qc, 1, 0) + qc.append(gate, [0, 1]) + qc.append(gate, [1, 0]) qc.measure_all() qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") @@ -97,17 +92,13 @@ def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Calla @pytest.mark.parametrize( "gate", - [ - QuantumCircuit.cy, - lambda qc, ctrl, target: QuantumCircuit.crx(qc, 0.5, ctrl, target), - lambda qc, ctrl, target: QuantumCircuit.cry(qc, 0.5, ctrl, target), - ], + [qiskit.circuit.library.CYGate(), qiskit.circuit.library.CRXGate(0.5), qiskit.circuit.library.CRYGate(0.5)], ) -def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: +def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: """Verify that control and target are flipped using two swap gates for some gates where this is possible.""" qc = QuantumCircuit(2) - gate(qc, 0, 1) - gate(qc, 1, 0) + qc.append(gate, [0, 1]) + qc.append(gate, [1, 0]) qc.measure_all() qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") @@ -117,17 +108,13 @@ def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Callable[ @pytest.mark.parametrize( "gate", - [ - QuantumCircuit.cz, - lambda qc, ctrl, target: QuantumCircuit.cp(qc, 0.5, ctrl, target), - lambda qc, ctrl, target: QuantumCircuit.crz(qc, 0.5, ctrl, target), - ], + [qiskit.circuit.library.CZGate(), qiskit.circuit.library.CPhaseGate(0.5), qiskit.circuit.library.CRZGate(0.5)], ) -def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: +def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: """Verify that control and target are flipped without adding additional for some gates where this is possible.""" qc = QuantumCircuit(2) - gate(qc, 0, 1) - gate(qc, 1, 0) + qc.append(gate, [0, 1]) + qc.append(gate, [1, 0]) qc.measure_all() qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") @@ -138,58 +125,27 @@ def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: Calla @pytest.mark.parametrize( "gate", [ - QuantumCircuit.cx, - QuantumCircuit.cy, - QuantumCircuit.cz, - QuantumCircuit.csx, - QuantumCircuit.ch, - QuantumCircuit.cnot, - QuantumCircuit.cs, - QuantumCircuit.csdg, - ], -) -def test_direction_reverse_params_0(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: - """Verify that control and target is flipped correctly for all two qubit controlled gates with zero parameters.""" - qc = QuantumCircuit(2) - gate(qc, 0, 1) - gate(qc, 1, 0) - qc.measure_all() - qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") - - result = verify(qc, qc_mapped) - assert result.considered_equivalent() is True - - -@pytest.mark.parametrize( - "gate", - [ - QuantumCircuit.cp, - QuantumCircuit.crx, - QuantumCircuit.cry, - QuantumCircuit.crz, + qiskit.circuit.library.CHGate(), + qiskit.circuit.library.CPhaseGate(0.25), + qiskit.circuit.library.CSdgGate(), + qiskit.circuit.library.CRXGate(0.25), + qiskit.circuit.library.CRYGate(0.25), + qiskit.circuit.library.CRZGate(0.25), + qiskit.circuit.library.CSGate(), + qiskit.circuit.library.CSXGate(), + qiskit.circuit.library.CU1Gate(0.25), + qiskit.circuit.library.CU3Gate(0.25, 0.5, 0.75), + qiskit.circuit.library.CUGate(0.2, 0.4, 0.6, 0.8), + qiskit.circuit.library.CXGate(), + qiskit.circuit.library.CYGate(), + qiskit.circuit.library.CZGate(), ], ) -def test_direction_reverse_params_1(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: - """Verify that control and target is flipped correctly for all two qubit controlled gates with one parameter.""" - qc = QuantumCircuit(2) - gate(qc, 0.25, 0, 1) - gate(qc, 0.75, 1, 0) - qc.measure_all() - qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") - - result = verify(qc, qc_mapped) - assert result.considered_equivalent() is True - - -@pytest.mark.parametrize( - "gate", - [QuantumCircuit.cu], -) -def test_direction_reverse_params_4(one_way_arch: qmap.Architecture, gate: Callable[..., Any]) -> None: - """Verify that control and target is flipped correctly for all two qubit controlled gates with four parameters.""" +def test_direction_reverse_verify(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: + """Verify that control and target is flipped correctly for two qubit controlled gates.""" qc = QuantumCircuit(2) - gate(qc, 0.1, 0.2, 0.3, 0.4, 0, 1) - gate(qc, 0.9, 0.8, 0.7, 0.6, 1, 0) + qc.append(gate, [0, 1]) + qc.append(gate, [1, 0]) qc.measure_all() qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") From 2bd795ecc9faad9e2b8fc0066d1c2a240236b53b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 7 Mar 2023 20:07:13 +0100 Subject: [PATCH 16/30] =?UTF-8?q?=F0=9F=A9=B9=20fix=20Python=20typing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/python/test_exact_mapper.py | 51 ++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 69739594f..8ff08e240 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -1,9 +1,16 @@ """Test the exact mapper.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + import pytest -import qiskit +import qiskit.circuit.library as qcl from qiskit import QuantumCircuit from qiskit.providers.fake_provider import FakeLondon +if TYPE_CHECKING: + from qiskit.circuit import ControlledGate + from mqt import qmap from mqt.qcec import verify @@ -76,9 +83,9 @@ def one_way_arch() -> qmap.Architecture: @pytest.mark.parametrize( "gate", - [qiskit.circuit.library.CXGate(), qiskit.circuit.library.CSXGate()], + [qcl.CXGate(), qcl.CSXGate()], ) -def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: +def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: """Verify that control and target are flipped using four hadamard gates for some gates where this is possible.""" qc = QuantumCircuit(2) qc.append(gate, [0, 1]) @@ -92,9 +99,9 @@ def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: qiski @pytest.mark.parametrize( "gate", - [qiskit.circuit.library.CYGate(), qiskit.circuit.library.CRXGate(0.5), qiskit.circuit.library.CRYGate(0.5)], + [qcl.CYGate(), qcl.CRXGate(0.5), qcl.CRYGate(0.5)], ) -def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: +def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: """Verify that control and target are flipped using two swap gates for some gates where this is possible.""" qc = QuantumCircuit(2) qc.append(gate, [0, 1]) @@ -108,9 +115,9 @@ def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: qiskit.ci @pytest.mark.parametrize( "gate", - [qiskit.circuit.library.CZGate(), qiskit.circuit.library.CPhaseGate(0.5), qiskit.circuit.library.CRZGate(0.5)], + [qcl.CZGate(), qcl.CPhaseGate(0.5), qcl.CRZGate(0.5)], ) -def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: +def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: """Verify that control and target are flipped without adding additional for some gates where this is possible.""" qc = QuantumCircuit(2) qc.append(gate, [0, 1]) @@ -125,23 +132,23 @@ def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: qiski @pytest.mark.parametrize( "gate", [ - qiskit.circuit.library.CHGate(), - qiskit.circuit.library.CPhaseGate(0.25), - qiskit.circuit.library.CSdgGate(), - qiskit.circuit.library.CRXGate(0.25), - qiskit.circuit.library.CRYGate(0.25), - qiskit.circuit.library.CRZGate(0.25), - qiskit.circuit.library.CSGate(), - qiskit.circuit.library.CSXGate(), - qiskit.circuit.library.CU1Gate(0.25), - qiskit.circuit.library.CU3Gate(0.25, 0.5, 0.75), - qiskit.circuit.library.CUGate(0.2, 0.4, 0.6, 0.8), - qiskit.circuit.library.CXGate(), - qiskit.circuit.library.CYGate(), - qiskit.circuit.library.CZGate(), + qcl.CHGate(), + qcl.CPhaseGate(0.25), + qcl.CSdgGate(), + qcl.CRXGate(0.25), + qcl.CRYGate(0.25), + qcl.CRZGate(0.25), + qcl.CSGate(), + qcl.CSXGate(), + qcl.CU1Gate(0.25), + qcl.CU3Gate(0.25, 0.5, 0.75), + qcl.CUGate(0.2, 0.4, 0.6, 0.8), + qcl.CXGate(), + qcl.CYGate(), + qcl.CZGate(), ], ) -def test_direction_reverse_verify(one_way_arch: qmap.Architecture, gate: qiskit.circuit.ControlledGate) -> None: +def test_direction_reverse_verify(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: """Verify that control and target is flipped correctly for two qubit controlled gates.""" qc = QuantumCircuit(2) qc.append(gate, [0, 1]) From 88a82ee576913e100cdf89ac47e12484f65daa6e Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 7 Mar 2023 20:28:48 +0100 Subject: [PATCH 17/30] =?UTF-8?q?=F0=9F=A9=B9=20silence=20an=20external=20?= =?UTF-8?q?Qiskit=20warning=20from=20the=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index df236f7e0..590a1a16c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,8 +107,7 @@ log_cli_level = "INFO" xfail_strict = true filterwarnings = [ "error", - # See https://github.com/Qiskit/rustworkx/pull/728 - 'ignore:RetworkxLoader.exec_module\(\) not found; falling back to load_module\(\):ImportWarning', + 'ignore:__package__ != __spec__.parent:ImportWarning', ] [tool.coverage.run] source = ["mqt.qmap"] From f0f19e36407fe6fd9c8d6e51f3bf00d680b7de9a Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 7 Mar 2023 20:47:28 +0100 Subject: [PATCH 18/30] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20restructure=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/python/test_exact_mapper.py | 56 ++++++++++++++------------------ 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 8ff08e240..153aea07b 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING +import numpy as np import pytest import qiskit.circuit.library as qcl from qiskit import QuantumCircuit @@ -96,10 +97,19 @@ def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Contr assert qc_mapped.count_ops()["h"] == 4 assert "swap" not in qc_mapped.count_ops() + result = verify(qc, qc_mapped) + assert result.considered_equivalent() is True + @pytest.mark.parametrize( "gate", - [qcl.CYGate(), qcl.CRXGate(0.5), qcl.CRYGate(0.5)], + [ + qcl.CYGate(), + qcl.CRXGate(0.5), + qcl.CRYGate(0.5), + qcl.CHGate(), + qcl.CU3Gate(0.25, 0.5, 0.75), + ], ) def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: """Verify that control and target are flipped using two swap gates for some gates where this is possible.""" @@ -112,49 +122,33 @@ def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Controlle assert "h" not in qc_mapped.count_ops() assert qc_mapped.count_ops()["swap"] == 2 - -@pytest.mark.parametrize( - "gate", - [qcl.CZGate(), qcl.CPhaseGate(0.5), qcl.CRZGate(0.5)], -) -def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: - """Verify that control and target are flipped without adding additional for some gates where this is possible.""" - qc = QuantumCircuit(2) - qc.append(gate, [0, 1]) - qc.append(gate, [1, 0]) - qc.measure_all() - - qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") - assert "h" not in qc_mapped.count_ops() - assert "swap" not in qc_mapped.count_ops() + result = verify(qc, qc_mapped) + assert result.considered_equivalent() is True @pytest.mark.parametrize( "gate", [ - qcl.CHGate(), - qcl.CPhaseGate(0.25), - qcl.CSdgGate(), - qcl.CRXGate(0.25), - qcl.CRYGate(0.25), - qcl.CRZGate(0.25), - qcl.CSGate(), - qcl.CSXGate(), - qcl.CU1Gate(0.25), - qcl.CU3Gate(0.25, 0.5, 0.75), - qcl.CUGate(0.2, 0.4, 0.6, 0.8), - qcl.CXGate(), - qcl.CYGate(), qcl.CZGate(), + qcl.CPhaseGate(0.5), + qcl.CRZGate(0.5), + qcl.CPhaseGate(np.pi / 2), + qcl.CPhaseGate(-np.pi / 2), + qcl.CPhaseGate(np.pi / 4), + qcl.CPhaseGate(-np.pi / 4), + qcl.CU1Gate(0.25), ], ) -def test_direction_reverse_verify(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: - """Verify that control and target is flipped correctly for two qubit controlled gates.""" +def test_direction_reverse_identity(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: + """Verify that control and target are flipped without adding gates for some gates where this is possible.""" qc = QuantumCircuit(2) qc.append(gate, [0, 1]) qc.append(gate, [1, 0]) qc.measure_all() + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + assert "h" not in qc_mapped.count_ops() + assert "swap" not in qc_mapped.count_ops() result = verify(qc, qc_mapped) assert result.considered_equivalent() is True From e71fb87e448a86c25aea74734fe637ea156dd0a4 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Sat, 11 Mar 2023 16:11:20 +0100 Subject: [PATCH 19/30] =?UTF-8?q?=F0=9F=90=9B=20Prevent=20reversals=20with?= =?UTF-8?q?=20swaps=20in=20exact=20mapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Architecture.hpp | 10 +-- include/Mapper.hpp | 2 + src/Architecture.cpp | 43 +++++------ src/Mapper.cpp | 16 ++--- src/exact/ExactMapper.cpp | 10 ++- test/python/test_exact_mapper.py | 6 +- test/test_exact.cpp | 119 +++++++++++++++++++++++++++++++ 7 files changed, 167 insertions(+), 39 deletions(-) diff --git a/include/Architecture.hpp b/include/Architecture.hpp index c4d9420bb..94b2b93e7 100644 --- a/include/Architecture.hpp +++ b/include/Architecture.hpp @@ -32,7 +32,7 @@ constexpr std::uint32_t COST_TELEPORTATION = 2 * COST_CNOT_GATE + COST_MEASUREMENT + 4 * COST_SINGLE_QUBIT_GATE; constexpr std::uint32_t COST_DIRECTION_REVERSE = 4 * COST_SINGLE_QUBIT_GATE; -enum class GateFlipStrategy { Unknown, Identity, Swap, Hadamard }; +enum class DirectionReversalStrategy { Identity, Hadamard, NotApplicable }; class Architecture { public: @@ -350,9 +350,11 @@ class Architecture { static void printCouplingMap(const CouplingMap& cm, std::ostream& os); - static GateFlipStrategy getGateFlipStrategy(qc::OpType opType); - static std::uint32_t computeCostDirectionReverse(qc::OpType opType); - static std::uint32_t computeGatesDirectionReverse(qc::OpType opType); + static DirectionReversalStrategy + getDirectionReversalStrategy(qc::OpType opType); + static std::uint32_t computeCostDirectionReverse(qc::OpType opType); + static std::uint32_t computeGatesDirectionReverse(qc::OpType opType); + static bool supportsDirectionReversal(qc::OpType opType); protected: std::string name; diff --git a/include/Mapper.hpp b/include/Mapper.hpp index 3bbb7751e..610a2e2f7 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -254,6 +254,8 @@ class Mapper { virtual MappingResults& getResults() { return results; } + qc::QuantumComputation& getMappedCircuit() { return qcMapped; } + virtual nlohmann::json json() { return results.json(); } virtual std::string csv() { return results.csv(); } diff --git a/src/Architecture.cpp b/src/Architecture.cpp index 13b40ad92..9232e47a7 100644 --- a/src/Architecture.cpp +++ b/src/Architecture.cpp @@ -688,14 +688,13 @@ void Architecture::printCouplingMap(const CouplingMap& cm, std::ostream& os) { os << "}" << std::endl; } -GateFlipStrategy Architecture::getGateFlipStrategy(qc::OpType opType) { +DirectionReversalStrategy +Architecture::getDirectionReversalStrategy(qc::OpType opType) { switch (opType) { - case qc::None: - return GateFlipStrategy::Unknown; case qc::X: case qc::SX: case qc::SXdag: - return GateFlipStrategy::Hadamard; + return DirectionReversalStrategy::Hadamard; case qc::Z: case qc::S: case qc::Sdag: @@ -704,35 +703,39 @@ GateFlipStrategy Architecture::getGateFlipStrategy(qc::OpType opType) { case qc::Phase: case qc::RZ: case qc::SWAP: - return GateFlipStrategy::Identity; + return DirectionReversalStrategy::Identity; default: - return GateFlipStrategy::Swap; + return DirectionReversalStrategy::NotApplicable; } } std::uint32_t Architecture::computeCostDirectionReverse(qc::OpType opType) { - switch (Architecture::getGateFlipStrategy(opType)) { - case GateFlipStrategy::Identity: + switch (Architecture::getDirectionReversalStrategy(opType)) { + case DirectionReversalStrategy::Identity: return 0; - case GateFlipStrategy::Hadamard: + case DirectionReversalStrategy::Hadamard: return 4 * COST_SINGLE_QUBIT_GATE; - case GateFlipStrategy::Swap: - case GateFlipStrategy::Unknown: - return 2 * COST_BIDIRECTIONAL_SWAP; default: - throw QMAPException("Unexpected GateFlipStrategy!"); + throw QMAPException( + "Cannot compute cost for direction reversal, because gate " + + toString(opType) + " does not support reversal!"); } } std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) { - switch (Architecture::getGateFlipStrategy(opType)) { - case GateFlipStrategy::Identity: + switch (Architecture::getDirectionReversalStrategy(opType)) { + case DirectionReversalStrategy::Identity: return 0; - case GateFlipStrategy::Hadamard: + case DirectionReversalStrategy::Hadamard: return 4; - case GateFlipStrategy::Swap: - case GateFlipStrategy::Unknown: - return 2 * GATES_OF_BIDIRECTIONAL_SWAP; default: - throw QMAPException("Unexpected GateFlipStrategy!"); + throw QMAPException( + "Cannot compute gates for direction reversal, because gate " + + toString(opType) + " does not support reversal!"); } } + +bool Architecture::supportsDirectionReversal(qc::OpType opType) { + const auto strategy = getDirectionReversalStrategy(opType); + return strategy == DirectionReversalStrategy::Identity || + strategy == DirectionReversalStrategy::Hadamard; +} diff --git a/src/Mapper.cpp b/src/Mapper.cpp index b8c9ab2a9..39d30eee9 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -335,18 +335,11 @@ std::size_t Mapper::insertGate(const Edge& edge, const qc::Operation& op) { std::size_t Mapper::insertFlippedGate(const Edge& edge, const qc::Operation& op) { std::size_t tmp = 0; - switch (Architecture::getGateFlipStrategy(op.getType())) { - case GateFlipStrategy::Identity: + switch (Architecture::getDirectionReversalStrategy(op.getType())) { + case DirectionReversalStrategy::Identity: // no additional gates required to reverse direction return insertGate(edge, op); - case GateFlipStrategy::Swap: - case GateFlipStrategy::Unknown: - // swap gates are used to reverse direction - qcMapped.swap(edge.first, edge.second); - tmp = insertGate(edge, op); - qcMapped.swap(edge.second, edge.first); - return 2 + tmp; - case GateFlipStrategy::Hadamard: + case DirectionReversalStrategy::Hadamard: // four hadamard gates to reverse direction qcMapped.h(edge.first); qcMapped.h(edge.second); @@ -355,6 +348,7 @@ std::size_t Mapper::insertFlippedGate(const Edge& edge, qcMapped.h(edge.second); return tmp + 4; default: - throw QMAPException("Unexpected GateFlipStrategy!"); + throw QMAPException("Cannot insert reversed gate, because gate " + + toString(op.getType()) + " does not support reversal!"); } } diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index e937690d4..3a76b86db 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -599,7 +599,12 @@ number of variables: (|L|-1) * m! auto indexSC = x[k][physicalQubitIndex[edge.second]] [static_cast(gate.control)]; - coupling = coupling || ((indexFC && indexST) || (indexFT && indexSC)); + if (Architecture::supportsDirectionReversal(gate.op->getType())) { + coupling = + coupling || ((indexFC && indexST) || (indexFT && indexSC)); + } else { + coupling = coupling || (indexFC && indexST); + } } } allCouplings = allCouplings && coupling; @@ -726,6 +731,9 @@ number of variables: (|L|-1) * m! if (gate.singleQubit()) { continue; } + if (!Architecture::supportsDirectionReversal(gate.op->getType())) { + continue; + } auto reverse = LogicTerm(false); for (const auto& [q0, q1] : rcm) { diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 153aea07b..07f4392eb 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -112,15 +112,15 @@ def test_direction_reverse_hadamard(one_way_arch: qmap.Architecture, gate: Contr ], ) def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: ControlledGate) -> None: - """Verify that control and target are flipped using two swap gates for some gates where this is possible.""" + """Verify that control and target are flipped using a swap permutation.""" qc = QuantumCircuit(2) qc.append(gate, [0, 1]) qc.append(gate, [1, 0]) qc.measure_all() - qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact", swap_limit=10, swap_reduction="custom") assert "h" not in qc_mapped.count_ops() - assert qc_mapped.count_ops()["swap"] == 2 + assert qc_mapped.count_ops()["swap"] == 1 result = verify(qc, qc_mapped) assert result.considered_equivalent() is True diff --git a/test/test_exact.cpp b/test/test_exact.cpp index 45133f408..4fb6d8c23 100644 --- a/test/test_exact.cpp +++ b/test/test_exact.cpp @@ -556,3 +556,122 @@ TEST_F(ExactTest, Test) { EXPECT_EQ(mapper.getResults().output.swaps, 0); EXPECT_EQ(mapper.getResults().output.directionReverse, 2); } + +TEST_F(ExactTest, TestDirectionReverseHadamard) { + using namespace qc::literals; + + Architecture arch; + const CouplingMap cm = {{0, 1}}; + arch.loadCouplingMap(2, cm); + + Architecture::printCouplingMap(cm, std::cout); + + qc = qc::QuantumComputation(2); + qc.sx(0, 1_pc); + qc.sx(1, 0_pc); + + auto mapper = ExactMapper(qc, arch); + settings.enableSwapLimits = false; + mapper.map(settings); + + EXPECT_EQ(mapper.getResults().output.swaps, 0); + EXPECT_EQ(mapper.getResults().output.directionReverse, 1); + int hadamard = 0; + int sx = 0; + for (auto& gate : mapper.getMappedCircuit()) { + switch (gate->getType()) { + case qc::H: + hadamard++; + break; + case qc::SX: + sx++; + break; + case qc::Barrier: + case qc::Measure: + break; + default: + FAIL() << "Unexpected OpType " << toString(gate->getType()) << "!"; + } + } + EXPECT_EQ(hadamard, 4); + EXPECT_EQ(sx, 2); +} + +TEST_F(ExactTest, TestDirectionReverseIdentity) { + using namespace qc::literals; + + Architecture arch; + const CouplingMap cm = {{0, 1}}; + arch.loadCouplingMap(2, cm); + + Architecture::printCouplingMap(cm, std::cout); + + qc = qc::QuantumComputation(2); + qc.rz(0, 1_pc, 0.25); + qc.rz(1, 0_pc, 0.75); + + auto mapper = ExactMapper(qc, arch); + settings.enableSwapLimits = false; + mapper.map(settings); + + mapper.dumpResult(std::cout, qc::Format::OpenQASM); + + EXPECT_EQ(mapper.getResults().output.swaps, 0); + EXPECT_EQ(mapper.getResults().output.directionReverse, 1); + int rz = 0; + for (auto& gate : mapper.getMappedCircuit()) { + switch (gate->getType()) { + case qc::RZ: + rz++; + break; + case qc::Barrier: + case qc::Measure: + break; + default: + FAIL() << "Unexpected OpType " << toString(gate->getType()) << "!"; + } + } + EXPECT_EQ(rz, 2); +} + +TEST_F(ExactTest, TestDirectionReverseNotApplicable) { + using namespace qc::literals; + + Architecture arch; + const CouplingMap cm = {{0, 1}}; + arch.loadCouplingMap(2, cm); + + Architecture::printCouplingMap(cm, std::cout); + + qc = qc::QuantumComputation(2); + qc.y(0, 1_pc); + qc.y(1, 0_pc); + + auto mapper = ExactMapper(qc, arch); + settings.enableSwapLimits = false; + mapper.map(settings); + + mapper.dumpResult(std::cout, qc::Format::OpenQASM); + + EXPECT_EQ(mapper.getResults().output.swaps, 1); + EXPECT_EQ(mapper.getResults().output.directionReverse, 0); + int swap = 0; + int y = 0; + for (auto& gate : mapper.getMappedCircuit()) { + switch (gate->getType()) { + case qc::SWAP: + swap++; + break; + case qc::Y: + y++; + break; + case qc::Barrier: + case qc::Measure: + break; + default: + FAIL() << "Unexpected OpType " << toString(gate->getType()) << "!"; + } + } + EXPECT_EQ(swap, 1); + EXPECT_EQ(y, 2); +} From 45deaab34c3f56a18e82daa7a4b1698acb56ae21 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 17:40:47 +0100 Subject: [PATCH 20/30] Update include/Mapper.hpp --- include/Mapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Mapper.hpp b/include/Mapper.hpp index 610a2e2f7..2fedd7dbd 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -157,7 +157,7 @@ class Mapper { countGates(circuit.cbegin(), circuit.cend(), info); } /** - * @brief count number of elementary gates and controlled gates in circuit and + * @brief count number of elementary gates and two-qubit gates in circuit and * save the results in `info.gates` and `info.twoQubitGates` */ virtual void countGates(decltype(qcMapped.cbegin()) it, From e43c357eae421e5205983a77ec2aeed9995886b0 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 17:50:03 +0100 Subject: [PATCH 21/30] =?UTF-8?q?=F0=9F=9A=A8=20some=20`const`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- src/Architecture.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Architecture.cpp b/src/Architecture.cpp index 9232e47a7..1583b664e 100644 --- a/src/Architecture.cpp +++ b/src/Architecture.cpp @@ -689,7 +689,7 @@ void Architecture::printCouplingMap(const CouplingMap& cm, std::ostream& os) { } DirectionReversalStrategy -Architecture::getDirectionReversalStrategy(qc::OpType opType) { +Architecture::getDirectionReversalStrategy(const qc::OpType opType) { switch (opType) { case qc::X: case qc::SX: @@ -709,7 +709,8 @@ Architecture::getDirectionReversalStrategy(qc::OpType opType) { } } -std::uint32_t Architecture::computeCostDirectionReverse(qc::OpType opType) { +std::uint32_t +Architecture::computeCostDirectionReverse(const qc::OpType opType) { switch (Architecture::getDirectionReversalStrategy(opType)) { case DirectionReversalStrategy::Identity: return 0; @@ -721,7 +722,8 @@ std::uint32_t Architecture::computeCostDirectionReverse(qc::OpType opType) { toString(opType) + " does not support reversal!"); } } -std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) { +std::uint32_t +Architecture::computeGatesDirectionReverse(const qc::OpType opType) { switch (Architecture::getDirectionReversalStrategy(opType)) { case DirectionReversalStrategy::Identity: return 0; @@ -734,7 +736,7 @@ std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) { } } -bool Architecture::supportsDirectionReversal(qc::OpType opType) { +bool Architecture::supportsDirectionReversal(const qc::OpType opType) { const auto strategy = getDirectionReversalStrategy(opType); return strategy == DirectionReversalStrategy::Identity || strategy == DirectionReversalStrategy::Hadamard; From 3c982bc7d355a67783cdd75dd5145261128e2273 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 17:51:02 +0100 Subject: [PATCH 22/30] =?UTF-8?q?=F0=9F=9A=9A=20rename=20`insertFlippedGat?= =?UTF-8?q?e`=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/Mapper.hpp | 2 +- src/Mapper.cpp | 12 ++++++------ src/exact/ExactMapper.cpp | 2 +- src/heuristic/HeuristicMapper.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/Mapper.hpp b/include/Mapper.hpp index 2fedd7dbd..fd7cac084 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -182,7 +182,7 @@ class Mapper { * @param op the operation which is inserted * @return total number of inserted operations */ - std::size_t insertFlippedGate(const Edge& edge, const qc::Operation& op); + std::size_t insertReversedGate(const Edge& edge, const qc::Operation& op); /** * @brief performs optimizations on the circuit before mapping diff --git a/src/Mapper.cpp b/src/Mapper.cpp index 39d30eee9..dc88edf84 100644 --- a/src/Mapper.cpp +++ b/src/Mapper.cpp @@ -332,21 +332,21 @@ std::size_t Mapper::insertGate(const Edge& edge, const qc::Operation& op) { return 1; } -std::size_t Mapper::insertFlippedGate(const Edge& edge, - const qc::Operation& op) { - std::size_t tmp = 0; +std::size_t Mapper::insertReversedGate(const Edge& edge, + const qc::Operation& op) { switch (Architecture::getDirectionReversalStrategy(op.getType())) { case DirectionReversalStrategy::Identity: // no additional gates required to reverse direction return insertGate(edge, op); - case DirectionReversalStrategy::Hadamard: + case DirectionReversalStrategy::Hadamard: { // four hadamard gates to reverse direction qcMapped.h(edge.first); qcMapped.h(edge.second); - tmp = insertGate(edge, op); + const auto nGates = insertGate(edge, op); qcMapped.h(edge.first); qcMapped.h(edge.second); - return tmp + 4; + return nGates + 4; + } default: throw QMAPException("Cannot insert reversed gate, because gate " + toString(op.getType()) + " does not support reversal!"); diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 3a76b86db..c7720619f 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -294,7 +294,7 @@ void ExactMapper::map(const Configuration& settings) { << " with control and target: " << controlledGate.first << " " << controlledGate.second << std::endl; } - insertFlippedGate(reverse, *op); + insertReversedGate(reverse, *op); } else { if (settings.verbose) { std::cout << i << ": Added controlled gate " << op->getName() diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index a64653c10..8024bdfdd 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -106,7 +106,7 @@ void HeuristicMapper::map(const Configuration& configuration) { ": " + std::to_string(reverse.first) + "-" + std::to_string(reverse.second)); } - gateidx += insertFlippedGate(reverse, *op); + gateidx += insertReversedGate(reverse, *op); results.output.directionReverse++; } else { gateidx += insertGate(controlledGate, *op); From 80609be5dc315e0802afcce11811dae55d791f2d Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 17:58:20 +0100 Subject: [PATCH 23/30] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20condition?= =?UTF-8?q?=20to=20simplify=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- src/exact/ExactMapper.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index c7720619f..238a5172c 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -583,7 +583,8 @@ number of variables: (|L|-1) * m! } auto coupling = LogicTerm(false); - if (architecture.bidirectional()) { + if (architecture.bidirectional() || + !Architecture::supportsDirectionReversal(gate.op->getType())) { for (const auto& edge : rcm) { auto indexFC = x[k][physicalQubitIndex[edge.first]] [static_cast(gate.control)]; @@ -598,13 +599,7 @@ number of variables: (|L|-1) * m! auto indexFT = x[k][physicalQubitIndex[edge.first]][gate.target]; auto indexSC = x[k][physicalQubitIndex[edge.second]] [static_cast(gate.control)]; - - if (Architecture::supportsDirectionReversal(gate.op->getType())) { - coupling = - coupling || ((indexFC && indexST) || (indexFT && indexSC)); - } else { - coupling = coupling || (indexFC && indexST); - } + coupling = coupling || ((indexFC && indexST) || (indexFT && indexSC)); } } allCouplings = allCouplings && coupling; From 44d81c81d80872b09e4e2794438c68279ebecf5b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 17:58:45 +0100 Subject: [PATCH 24/30] =?UTF-8?q?=E2=9A=A1=20add=20missing=20conditional?= =?UTF-8?q?=20skip=20statement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- src/exact/ExactMapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 238a5172c..957ced177 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -836,6 +836,9 @@ number of variables: (|L|-1) * m! if (gate.singleQubit()) { continue; } + if (!Architecture::supportsDirectionReversal(gate.op->getType())) { + continue; + } for (const auto& edge : rcm) { auto indexFT = x[k][physicalQubitIndex[edge.first]][gate.target]; auto indexSC = x[k][physicalQubitIndex[edge.second]] From 31e73b3b00f04cbf9f6ba8a4235e2fd30b121399 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 18:02:50 +0100 Subject: [PATCH 25/30] =?UTF-8?q?=F0=9F=9A=B8=20add=20additional=20sanity?= =?UTF-8?q?=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- src/exact/ExactMapper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 957ced177..0ab06c274 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -288,6 +288,12 @@ void ExactMapper::map(const Configuration& settings) { ": " + std::to_string(reverse.first) + "-" + std::to_string(reverse.second)); } + if (!Architecture::supportsDirectionReversal(op->getType())) { + throw QMAPException("Invalid controlled gate " + op->getName() + + ": " + std::to_string(reverse.first) + "-" + + std::to_string(reverse.second)); + } + if (settings.verbose) { std::cout << i << ": Added (direction-reversed) controlled gate " << op->getName() From a33e1d1e20a1fcc12d166daafad16db75d1f16f5 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 18:04:42 +0100 Subject: [PATCH 26/30] =?UTF-8?q?=F0=9F=9A=B8=20add=20`const`=20version=20?= =?UTF-8?q?of=20the=20mapped=20circuit=20getter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/Mapper.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/Mapper.hpp b/include/Mapper.hpp index fd7cac084..a0b24c78d 100644 --- a/include/Mapper.hpp +++ b/include/Mapper.hpp @@ -254,7 +254,8 @@ class Mapper { virtual MappingResults& getResults() { return results; } - qc::QuantumComputation& getMappedCircuit() { return qcMapped; } + const qc::QuantumComputation& getMappedCircuit() const { return qcMapped; } + qc::QuantumComputation& getMappedCircuit() { return qcMapped; } virtual nlohmann::json json() { return results.json(); } From 12ab7fab761c74460ebd26297546704ef6bbc6ec Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 18:12:32 +0100 Subject: [PATCH 27/30] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20simplify=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- test/python/test_exact_mapper.py | 2 +- test/test_exact.cpp | 89 ++++++++------------------------ 2 files changed, 22 insertions(+), 69 deletions(-) diff --git a/test/python/test_exact_mapper.py b/test/python/test_exact_mapper.py index 07f4392eb..52a3d7cb5 100644 --- a/test/python/test_exact_mapper.py +++ b/test/python/test_exact_mapper.py @@ -118,7 +118,7 @@ def test_direction_reverse_swap(one_way_arch: qmap.Architecture, gate: Controlle qc.append(gate, [1, 0]) qc.measure_all() - qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact", swap_limit=10, swap_reduction="custom") + qc_mapped, results = qmap.compile(qc, arch=one_way_arch, method="exact") assert "h" not in qc_mapped.count_ops() assert qc_mapped.count_ops()["swap"] == 1 diff --git a/test/test_exact.cpp b/test/test_exact.cpp index 4fb6d8c23..79f4f5c90 100644 --- a/test/test_exact.cpp +++ b/test/test_exact.cpp @@ -570,31 +570,15 @@ TEST_F(ExactTest, TestDirectionReverseHadamard) { qc.sx(0, 1_pc); qc.sx(1, 0_pc); - auto mapper = ExactMapper(qc, arch); - settings.enableSwapLimits = false; + auto mapper = ExactMapper(qc, arch); mapper.map(settings); - EXPECT_EQ(mapper.getResults().output.swaps, 0); - EXPECT_EQ(mapper.getResults().output.directionReverse, 1); - int hadamard = 0; - int sx = 0; - for (auto& gate : mapper.getMappedCircuit()) { - switch (gate->getType()) { - case qc::H: - hadamard++; - break; - case qc::SX: - sx++; - break; - case qc::Barrier: - case qc::Measure: - break; - default: - FAIL() << "Unexpected OpType " << toString(gate->getType()) << "!"; - } - } - EXPECT_EQ(hadamard, 4); - EXPECT_EQ(sx, 2); + const auto& results = mapper.getResults(); + EXPECT_EQ(results.output.swaps, 0); + EXPECT_EQ(results.output.directionReverse, 1); + const auto& qcMapped = mapper.getMappedCircuit(); + EXPECT_EQ(qcMapped.getNops(), 6U + 1U + 2U); + std::cout << qcMapped << std::endl; } TEST_F(ExactTest, TestDirectionReverseIdentity) { @@ -610,28 +594,15 @@ TEST_F(ExactTest, TestDirectionReverseIdentity) { qc.rz(0, 1_pc, 0.25); qc.rz(1, 0_pc, 0.75); - auto mapper = ExactMapper(qc, arch); - settings.enableSwapLimits = false; + auto mapper = ExactMapper(qc, arch); mapper.map(settings); - mapper.dumpResult(std::cout, qc::Format::OpenQASM); - - EXPECT_EQ(mapper.getResults().output.swaps, 0); - EXPECT_EQ(mapper.getResults().output.directionReverse, 1); - int rz = 0; - for (auto& gate : mapper.getMappedCircuit()) { - switch (gate->getType()) { - case qc::RZ: - rz++; - break; - case qc::Barrier: - case qc::Measure: - break; - default: - FAIL() << "Unexpected OpType " << toString(gate->getType()) << "!"; - } - } - EXPECT_EQ(rz, 2); + const auto& results = mapper.getResults(); + EXPECT_EQ(results.output.swaps, 0); + EXPECT_EQ(results.output.directionReverse, 1); + const auto& qcMapped = mapper.getMappedCircuit(); + EXPECT_EQ(qcMapped.getNops(), 2U + 1U + 2U); + std::cout << qcMapped << std::endl; } TEST_F(ExactTest, TestDirectionReverseNotApplicable) { @@ -647,31 +618,13 @@ TEST_F(ExactTest, TestDirectionReverseNotApplicable) { qc.y(0, 1_pc); qc.y(1, 0_pc); - auto mapper = ExactMapper(qc, arch); - settings.enableSwapLimits = false; + auto mapper = ExactMapper(qc, arch); mapper.map(settings); - mapper.dumpResult(std::cout, qc::Format::OpenQASM); - - EXPECT_EQ(mapper.getResults().output.swaps, 1); - EXPECT_EQ(mapper.getResults().output.directionReverse, 0); - int swap = 0; - int y = 0; - for (auto& gate : mapper.getMappedCircuit()) { - switch (gate->getType()) { - case qc::SWAP: - swap++; - break; - case qc::Y: - y++; - break; - case qc::Barrier: - case qc::Measure: - break; - default: - FAIL() << "Unexpected OpType " << toString(gate->getType()) << "!"; - } - } - EXPECT_EQ(swap, 1); - EXPECT_EQ(y, 2); + const auto& results = mapper.getResults(); + EXPECT_EQ(results.output.swaps, 1); + EXPECT_EQ(results.output.directionReverse, 0); + const auto& qcMapped = mapper.getMappedCircuit(); + EXPECT_EQ(qcMapped.getNops(), 3U + 1U + 2U); + std::cout << qcMapped << std::endl; } From 0aec5195f7cd3f31f6c0f4c354a10fd394725209 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Sat, 11 Mar 2023 18:41:18 +0100 Subject: [PATCH 28/30] =?UTF-8?q?=F0=9F=90=9B=20fix=20coupling=20limit=20c?= =?UTF-8?q?omputation=20for=20directed=20architectures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/Architecture.hpp | 13 +++++++------ src/Architecture.cpp | 22 +++++++++++++--------- src/exact/ExactMapper.cpp | 6 ++++++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/Architecture.hpp b/include/Architecture.hpp index 94b2b93e7..cc382eb4f 100644 --- a/include/Architecture.hpp +++ b/include/Architecture.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include constexpr std::uint8_t GATES_OF_BIDIRECTIONAL_SWAP = 3U; @@ -400,10 +401,10 @@ class Architecture { static std::size_t findCouplingLimit(const CouplingMap& cm, std::uint16_t nQubits); static std::size_t - findCouplingLimit(const CouplingMap& cm, std::uint16_t nQubits, - const std::set& qubitChoice); - static void - findCouplingLimit(std::uint16_t node, std::uint16_t curSum, - const std::vector>& connections, - std::vector& d, std::vector& visited); + findCouplingLimit(const CouplingMap& cm, std::uint16_t nQubits, + const std::set& qubitChoice); + static void findCouplingLimit( + std::uint16_t node, std::uint16_t curSum, + const std::vector>& connections, + std::vector& d, std::vector& visited); }; diff --git a/src/Architecture.cpp b/src/Architecture.cpp index 1583b664e..2c60344ec 100644 --- a/src/Architecture.cpp +++ b/src/Architecture.cpp @@ -486,13 +486,15 @@ std::uint64_t Architecture::bfs(const std::uint16_t start, std::size_t Architecture::findCouplingLimit(const CouplingMap& cm, const std::uint16_t nQubits) { - std::vector> connections; - std::vector d; - std::vector visited; + std::vector> connections; + std::vector d; + std::vector visited; connections.resize(nQubits); std::uint16_t maxSum = 0; for (const auto& edge : cm) { - connections.at(edge.first).emplace_back(edge.second); + connections.at(edge.first).emplace(edge.second); + // make sure that the connections are bidirectional + connections.at(edge.second).emplace(edge.first); } for (std::uint16_t q = 0; q < nQubits; ++q) { d.clear(); @@ -513,15 +515,17 @@ std::size_t Architecture::findCouplingLimit(const CouplingMap& cm, std::size_t Architecture::findCouplingLimit(const CouplingMap& cm, const std::uint16_t nQubits, const QubitSubset& qubitChoice) { - std::vector> connections; - std::vector d; - std::vector visited; + std::vector> connections; + std::vector d; + std::vector visited; connections.resize(nQubits); std::uint16_t maxSum = 0; for (const auto& edge : cm) { if ((qubitChoice.count(edge.first) != 0U) && (qubitChoice.count(edge.second) != 0U)) { - connections.at(edge.first).emplace_back(edge.second); + connections.at(edge.first).emplace(edge.second); + // make sure that the connections are bidirectional + connections.at(edge.second).emplace(edge.first); } } for (std::uint16_t q = 0; q < nQubits; ++q) { @@ -545,7 +549,7 @@ std::size_t Architecture::findCouplingLimit(const CouplingMap& cm, void Architecture::findCouplingLimit( const std::uint16_t node, const std::uint16_t curSum, - const std::vector>& connections, + const std::vector>& connections, std::vector& d, std::vector& visited) { if (visited.at(node)) { return; diff --git a/src/exact/ExactMapper.cpp b/src/exact/ExactMapper.cpp index 0ab06c274..717ea54e3 100644 --- a/src/exact/ExactMapper.cpp +++ b/src/exact/ExactMapper.cpp @@ -103,6 +103,12 @@ void ExactMapper::map(const Configuration& settings) { } else { maxLimit = architecture.getCouplingLimit() - 1U; } + if (!architecture.bidirectional()) { + // on a directed architecture, one more SWAP might be needed overall + // due to the directionality of the edges and direction reversal not + // being possible for every gate. + maxLimit += 1U; + } if (config.swapReduction == SwapReduction::CouplingLimit) { limit = maxLimit; } else if (config.swapReduction == SwapReduction::Increasing) { From 8897aa46c7418b81c1906c41d3a7a1c0c3bde655 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:57:00 +0100 Subject: [PATCH 29/30] =?UTF-8?q?=F0=9F=A7=AA=20Add=20direction=20reverse?= =?UTF-8?q?=20tests=20for=20heuristic=20mapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/test_heuristic.cpp | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/test_heuristic.cpp b/test/test_heuristic.cpp index 84ece675e..04d7de82d 100644 --- a/test/test_heuristic.cpp +++ b/test/test_heuristic.cpp @@ -250,3 +250,87 @@ TEST_P(HeuristicTest20QTeleport, Teleportation) { tokyoMapper->printResult(std::cout); SUCCEED() << "Mapping successful"; } + +class DirectionReverseTest : public testing::TestWithParam { +protected: + qc::QuantumComputation qc{}; + Configuration settings{}; + + void SetUp() override { + using namespace qc::literals; + settings.verbose = true; + settings.method = Method::Heuristic; + } +}; + +TEST_F(DirectionReverseTest, TestDirectionReverseHadamard) { + using namespace qc::literals; + + Architecture arch; + const CouplingMap cm = {{0, 1}}; + arch.loadCouplingMap(2, cm); + + Architecture::printCouplingMap(cm, std::cout); + + qc = qc::QuantumComputation(2); + qc.sx(0, 1_pc); + qc.sx(1, 0_pc); + + auto mapper = HeuristicMapper(qc, arch); + mapper.map(settings); + + const auto& results = mapper.getResults(); + EXPECT_EQ(results.output.swaps, 0); + EXPECT_EQ(results.output.directionReverse, 1); + const auto& qcMapped = mapper.getMappedCircuit(); + EXPECT_EQ(qcMapped.getNops(), 6U + 1U + 2U); + std::cout << qcMapped << std::endl; +} + +TEST_F(DirectionReverseTest, TestDirectionReverseIdentity) { + using namespace qc::literals; + + Architecture arch; + const CouplingMap cm = {{0, 1}}; + arch.loadCouplingMap(2, cm); + + Architecture::printCouplingMap(cm, std::cout); + + qc = qc::QuantumComputation(2); + qc.rz(0, 1_pc, 0.25); + qc.rz(1, 0_pc, 0.75); + + auto mapper = HeuristicMapper(qc, arch); + mapper.map(settings); + + const auto& results = mapper.getResults(); + EXPECT_EQ(results.output.swaps, 0); + EXPECT_EQ(results.output.directionReverse, 1); + const auto& qcMapped = mapper.getMappedCircuit(); + EXPECT_EQ(qcMapped.getNops(), 2U + 1U + 2U); + std::cout << qcMapped << std::endl; +} + +TEST_F(DirectionReverseTest, TestDirectionReverseNotApplicable) { + using namespace qc::literals; + + Architecture arch; + const CouplingMap cm = {{0, 1}}; + arch.loadCouplingMap(2, cm); + + Architecture::printCouplingMap(cm, std::cout); + + qc = qc::QuantumComputation(2); + qc.y(0, 1_pc); + qc.y(1, 0_pc); + + auto mapper = HeuristicMapper(qc, arch); + mapper.map(settings); + + const auto& results = mapper.getResults(); + EXPECT_EQ(results.output.swaps, 1); + EXPECT_EQ(results.output.directionReverse, 0); + const auto& qcMapped = mapper.getMappedCircuit(); + EXPECT_EQ(qcMapped.getNops(), 3U + 1U + 2U); + std::cout << qcMapped << std::endl; +} From 34cc4bd875758390fe339803cc91555131903d42 Mon Sep 17 00:00:00 2001 From: Joachim Marin <55003364+JoachimMarin@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:59:47 +0100 Subject: [PATCH 30/30] =?UTF-8?q?=E2=9C=A8=20Update=20heuristic=20algorith?= =?UTF-8?q?m=20for=20direction=20reverse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Architecture.hpp | 35 +++++++++++++++-------- include/heuristic/HeuristicMapper.hpp | 41 +++++++++++++++++---------- include/utils.hpp | 16 ++++++----- src/heuristic/HeuristicMapper.cpp | 25 ++++++++++------ src/utils.cpp | 12 ++++---- 5 files changed, 81 insertions(+), 48 deletions(-) diff --git a/include/Architecture.hpp b/include/Architecture.hpp index cc382eb4f..800ffb220 100644 --- a/include/Architecture.hpp +++ b/include/Architecture.hpp @@ -245,7 +245,9 @@ class Architecture { return teleportationQubits; } - [[nodiscard]] const Matrix& getDistanceTable() const { return distanceTable; } + [[nodiscard]] const DistanceTable& getDistanceTable() const { + return distanceTable; + } [[nodiscard]] const Properties& getProperties() const { return properties; } @@ -282,10 +284,19 @@ class Architecture { singleQubitFidelities.clear(); } - [[nodiscard]] double distance(std::uint16_t control, - std::uint16_t target) const { + [[nodiscard]] double distance(std::uint16_t control, std::uint16_t target, + const qc::OpType opType) const { if (currentTeleportations.empty()) { - return distanceTable.at(control).at(target); + const auto& distanceEntry = distanceTable.at(control).at(target); + auto dist = distanceEntry.first; + if (distanceEntry.second && opType != qc::None) { + if (supportsDirectionReversal(opType)) { + dist += computeCostDirectionReverse(opType); + } else { + dist += 10000; + } + } + return dist; } return static_cast(bfs(control, target, currentTeleportations)); } @@ -363,7 +374,7 @@ class Architecture { CouplingMap couplingMap = {}; CouplingMap currentTeleportations = {}; bool isBidirectional = true; - Matrix distanceTable = {}; + DistanceTable distanceTable = {}; std::vector> teleportationQubits{}; Properties properties = {}; Matrix fidelityTable = {}; @@ -372,23 +383,23 @@ class Architecture { void createDistanceTable(); void createFidelityTable(); - static double costHeuristicBidirectional(const Dijkstra::Node& node) { + static std::pair + costHeuristicBidirectional(const Dijkstra::Node& node) { auto length = node.cost - 1; if (node.containsCorrectEdge) { - return length * COST_BIDIRECTIONAL_SWAP; + return {length * COST_BIDIRECTIONAL_SWAP, false}; } throw QMAPException("In a bidrectional architecture it should not happen " "that a node does not contain the right edge."); } - static double costHeuristicUnidirectional(const Dijkstra::Node& node) { + static std::pair + costHeuristicUnidirectional(const Dijkstra::Node& node) { auto length = node.cost - 1; if (node.containsCorrectEdge) { - return length * COST_UNIDIRECTIONAL_SWAP; + return {length * COST_UNIDIRECTIONAL_SWAP, false}; } - // TODO: distance has no gate context, but gates have different costs - return length * COST_UNIDIRECTIONAL_SWAP + - Architecture::computeCostDirectionReverse(qc::OpType::X); + return {length * COST_UNIDIRECTIONAL_SWAP, true}; } // added for teleportation diff --git a/include/heuristic/HeuristicMapper.hpp b/include/heuristic/HeuristicMapper.hpp index 3b3203bbf..b6a45ac2a 100644 --- a/include/heuristic/HeuristicMapper.hpp +++ b/include/heuristic/HeuristicMapper.hpp @@ -179,20 +179,28 @@ class HeuristicMapper : public Mapper { continue; } - auto cost = arch.distance( - static_cast( - locations.at(static_cast(gate.control))), - static_cast(locations.at(gate.target))); + auto cost = + arch.distance(static_cast(locations.at( + static_cast(gate.control))), + static_cast(locations.at(gate.target)), + gate.op->getType()); auto fidelityCost = cost; if (admissibleHeuristic) { costHeur = std::max(costHeur, fidelityCost); } else { costHeur += fidelityCost; } - if (cost > - Architecture::computeCostDirectionReverse(gate.op->getType())) { - done = false; - return; + if (Architecture::supportsDirectionReversal(gate.op->getType())) { + if (cost > + Architecture::computeCostDirectionReverse(gate.op->getType())) { + done = false; + return; + } + } else { + if (cost > COST_UNIDIRECTIONAL_SWAP * 2) { + done = false; + return; + } } } } @@ -231,20 +239,22 @@ class HeuristicMapper : public Mapper { * @brief returns distance of the given logical qubit pair according to the * current mapping */ - double distanceOnArchitectureOfLogicalQubits(std::uint16_t control, - std::uint16_t target) { + double distanceOnArchitectureOfLogicalQubits(std::uint16_t control, + std::uint16_t target, + const qc::OpType opType) { return architecture.distance( static_cast(locations.at(control)), - static_cast(locations.at(target))); + static_cast(locations.at(target)), opType); } /** * @brief returns distance of the given physical qubit pair on the * architecture */ - double distanceOnArchitectureOfPhysicalQubits(std::uint16_t control, - std::uint16_t target) { - return architecture.distance(control, target); + double distanceOnArchitectureOfPhysicalQubits(std::uint16_t control, + std::uint16_t target, + const qc::OpType opType) { + return architecture.distance(control, target, opType); } /** @@ -255,7 +265,8 @@ class HeuristicMapper : public Mapper { * to `target` * @param target an unmapped logical qubit */ - virtual void mapToMinDistance(std::uint16_t source, std::uint16_t target); + virtual void mapToMinDistance(std::uint16_t source, std::uint16_t target, + const qc::OpType opType); /** * @brief gathers all qubits that are acted on by a 2-qubit-gate in the given diff --git a/include/utils.hpp b/include/utils.hpp index 780e937db..526b2d01c 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -20,10 +20,11 @@ #include #include -using Matrix = std::vector>; -using Edge = std::pair; -using CouplingMap = std::set; -using QubitSubset = std::set; +using Matrix = std::vector>; +using DistanceTable = std::vector>>; +using Edge = std::pair; +using CouplingMap = std::set; +using QubitSubset = std::set; struct Exchange { Exchange(const std::uint16_t f, const std::uint16_t s, const qc::OpType type) @@ -60,9 +61,10 @@ class Dijkstra { double cost = -1.; }; - static void buildTable(std::uint16_t n, const CouplingMap& couplingMap, - Matrix& distanceTable, - const std::function& cost); + static void + buildTable(std::uint16_t n, const CouplingMap& couplingMap, + DistanceTable& distanceTable, + const std::function(const Node&)>& cost); protected: static void dijkstra(const CouplingMap& couplingMap, std::vector& nodes, diff --git a/src/heuristic/HeuristicMapper.cpp b/src/heuristic/HeuristicMapper.cpp index 8024bdfdd..e9009a371 100644 --- a/src/heuristic/HeuristicMapper.cpp +++ b/src/heuristic/HeuristicMapper.cpp @@ -327,7 +327,8 @@ void HeuristicMapper::mapUnmappedGates( for (std::uint16_t j = i + 1; j < architecture.getNqubits(); j++) { if (qubits.at(i) == DEFAULT_POSITION && qubits.at(j) == DEFAULT_POSITION) { - const double dist = architecture.distance(i, j); + const double dist = + architecture.distance(i, j, gate.op->getType()); if (dist < bestScore) { bestScore = dist; chosenEdge = std::make_pair(i, j); @@ -358,22 +359,25 @@ void HeuristicMapper::mapUnmappedGates( chosenEdge.second, qcMapped.outputPermutation); } else if (controlLocation == DEFAULT_POSITION) { - mapToMinDistance(gate.target, static_cast(gate.control)); + mapToMinDistance(gate.target, static_cast(gate.control), + gate.op->getType()); } else if (targetLocation == DEFAULT_POSITION) { - mapToMinDistance(static_cast(gate.control), gate.target); + mapToMinDistance(static_cast(gate.control), gate.target, + gate.op->getType()); } } } void HeuristicMapper::mapToMinDistance(const std::uint16_t source, - const std::uint16_t target) { + const std::uint16_t target, + const qc::OpType opType) { auto min = std::numeric_limits::max(); std::optional pos = std::nullopt; for (std::uint16_t i = 0; i < architecture.getNqubits(); ++i) { if (qubits.at(i) == DEFAULT_POSITION) { // TODO: Consider fidelity here if available auto distance = distanceOnArchitectureOfPhysicalQubits( - static_cast(locations.at(source)), i); + static_cast(locations.at(source)), i, opType); if (distance < min) { min = distance; pos = i; @@ -557,8 +561,10 @@ void HeuristicMapper::lookahead(const std::size_t layer, if (node.qubits.at(j) == DEFAULT_POSITION) { // TODO: Consider fidelity here if available min = std::min(min, distanceOnArchitectureOfPhysicalQubits( - j, static_cast( - node.locations.at(gate.target)))); + j, + static_cast( + node.locations.at(gate.target)), + gate.op->getType())); } } penalty = heuristicAddition(penalty, min); @@ -571,7 +577,7 @@ void HeuristicMapper::lookahead(const std::size_t layer, distanceOnArchitectureOfPhysicalQubits( static_cast(node.locations.at( static_cast(gate.control))), - j)); + j, gate.op->getType())); } } penalty = heuristicAddition(penalty, min); @@ -579,7 +585,8 @@ void HeuristicMapper::lookahead(const std::size_t layer, auto cost = architecture.distance( static_cast( node.locations.at(static_cast(gate.control))), - static_cast(node.locations.at(gate.target))); + static_cast(node.locations.at(gate.target)), + gate.op->getType()); penalty = heuristicAddition(penalty, cost); } } diff --git a/src/utils.cpp b/src/utils.cpp index d881d3fb6..90b20895b 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -7,11 +7,13 @@ #include -void Dijkstra::buildTable(const std::uint16_t n, const CouplingMap& couplingMap, - Matrix& distanceTable, - const std::function& cost) { +void Dijkstra::buildTable( + const std::uint16_t n, const CouplingMap& couplingMap, + DistanceTable& distanceTable, + const std::function(const Node&)>& cost) { distanceTable.clear(); - distanceTable.resize(n, std::vector(n, -1.)); + distanceTable.resize(n, std::vector>( + n, std::pair(-1., false))); for (std::uint16_t i = 0; i < n; ++i) { std::vector nodes(n); @@ -28,7 +30,7 @@ void Dijkstra::buildTable(const std::uint16_t n, const CouplingMap& couplingMap, for (std::uint16_t j = 0; j < n; ++j) { if (i == j) { - distanceTable.at(i).at(j) = 0; + distanceTable.at(i).at(j) = std::pair(0, false); } else { distanceTable.at(i).at(j) = cost(nodes.at(j)); }