diff --git a/include/DDMinimizer.hpp b/include/DDMinimizer.hpp new file mode 100644 index 00000000..50498e74 --- /dev/null +++ b/include/DDMinimizer.hpp @@ -0,0 +1,214 @@ +#include "Definitions.hpp" +#include "ir/Permutation.hpp" +#include "ir/QuantumComputation.hpp" + +#include +#include +#include +#include +#include + +namespace qc { +class DDMinimizer { + +public: + /** + * @brief Changes the order of qubits in a QuantumComputation to heuristically + * optimize for short running times of the DD-simulator. + * @detail Computes an initialLayout for the QuantumComputation based on a + * heuristic to optimize the running time of the DD-simulator. After that, the + * initialLayout is applied, i.e. the qubits in the QuantumComputation are + * re-ordered such that the resulting QuantumComputation's initialLayout is + * the identity again. The current implementation is based on patterns found + * in the controlled gates. + * @param param qc is the QuantumComputation to optimize the layout for + */ + static void optimizeInputPermutation(qc::QuantumComputation& qc); + + /** + * @details First collects operation indices of controlled operation for + * patterns (s. makeDataStructure). Then, based on the pattern of the + * controlled gates, the layout is adjusted. If no pattern is found, the + * control based permutation is created. + * @param qc is the QuantumComputation to optimize the layout for + * @return the qc::Permutation The computed permutation to be used as the + * initialLayout for a QuantumComputation + */ + static qc::Permutation createGateBasedPermutation(qc::QuantumComputation& qc); + +private: + // The data structure for the pattern analysis of controlled gates: + /** + *The ladder x_c and c_x describe for three qubits the following controlled + gates (c: control qubit, x: target qubit): + + * c_x: c | 0 1 2 + x | 1 2 3 + + * x_c: c | 1 2 3 + x | 0 1 2 + */ + std::map, int> xCMap; + std::map, int> cXMap; + + /** + *The ladders c_l, c_r, x_l, x_r consist of several steps, hence the vector + of maps. They describe for three qubits the following controlled gates (c: + control qubit, x: target qubit): + * c_l_1: c | 0 0 0 and c_l_2: c | 1 1 and c_l_3: c | 2 + x | 1 2 3 x | 2 3 x | 3 + + * c_r_1: c | 3 3 3 and c_r_2: c | 2 2 and c_r_3: c | 1 + x | 0 1 2 x | 0 1 x | 0 + + * x_l_1: c | 1 2 3 and x_l_2: c | 2 3 and x_l_3: c | 3 + x | 0 0 0 x | 1 1 x | 2 + + * x_r_1: c | 0 1 2 and x_r_2: c | 0 1 and x_r_3: c | 0 + x | 3 3 3 x | 2 2 x | 1 + */ + std::vector, int>> cLMap; + std::vector, int>> cHMap; + std::vector, int>> xLMap; + std::vector, int>> xHMap; + + // Helper functions for createGateBasedPermutation + /** + * @brief creates a data structure for the pattern analysis of controlled gates + * @details the data structure consists of two maps: + * 1. map: string of ladder (step) name to control and target bit to the index + of the operation + * 2. map: string of ladder name to vector: max index of operation for each + step or the c_x or x_c ladder + * for c_l and x_l position 0 in the vector marks the line of c(x) at 0 -> we + count the left most as the first + * for c_r and x_r position 0 in the vector marks the line of c(x) at bits - 1 + -> we count theright most as the first + * + * The ladder (steps) describe the following controlled gates (c: control + qubit, x: target qubit): e.g. for three qubits + * c_x: c | 0 1 2 + x | 1 2 3 + + * x_c: c | 1 2 3 + x | 0 1 2 + + * c_l_1: c | 0 0 0 and c_l_2: c | 1 1 and c_l_3: c | 2 + x | 1 2 3 x | 2 3 x | 3 + + * c_r_1: c | 3 3 3 and c_r_2: c | 2 2 and c_r_3: c | 1 + x | 0 1 2 x | 0 1 x | 0 + + * x_l_1: c | 1 2 3 and x_l_2: c | 2 3 and x_l_3: c | 3 + x | 0 0 0 x | 1 1 x | 2 + + * x_r_1: c | 0 1 2 and x_r_2: c | 0 1 and x_r_3: c | 0 + x | 3 3 3 x | 2 2 x | 1 + */ + + /** + * @brief initializes the data structure for the pattern analysis of + * controlled gates + * @details The function sets the qubits of the xCMap, cXMap, cLMap, cHMap, + * xLMap, xHMap depending on the number of qubits in the QuantumComputation + * @param bits is the number of qubits in the QuantumComputation + */ + void initializeDataStructure(std::size_t bits); + + /** + * @brief Helper function to find the maximum Instruction index of the + * complete patterns + * @details Returns the max index if the pattern is complete, otherwise -1 + * @param map of the pattern map + * @return the maximum index + */ + static int findMaxIndex(const std::map, int>& map); + + // Functions to analyze the pattern of the controlled gates + static bool isFullLadder(const std::vector& vec); + static std::size_t getStairCount(const std::vector& vec); + static int getLadderPosition(const std::vector& vec, int ladder); + + // Functions to adjust the layout based on the pattern of the controlled + // gates: + + /** + * @brief Helper function to reverse the layout (q: qubit, l: layer) + * @details q | 0 1 2 3 turns to q | 0 1 2 3 + * l | 0 1 2 3 l | 3 2 1 0 + * @param layout + * @return the reversed layout + */ + static std::vector reverseLayout(std::vector& layout); + + /** + * @brief Helper function to rotate the layout to the right (q: qubit, l: + * layer) + * @details q | 0 1 2 3 and 1 stairs turns to q | 0 1 2 3 + * l | 0 1 2 3 l | 1 2 3 0 + * @param layout, stairs (number of steps to rotate) + * @return the rotated layout + */ + static std::vector rotateRight(std::vector layout, + std::size_t stairs); + + /** + * @brief Helper function to rotate the layout to the left (q: qubit, l: + * layer) + * @details q | 0 1 2 3 and 2 stairs turns to q | 0 1 2 3 + * l | 0 1 2 3 l | 3 0 1 2 + * @param layout, stairs (number of steps to rotate) + * @return the rotated layout + */ + static std::vector rotateLeft(std::vector layout, + std::size_t stairs); + + /** + * @brief creates a Heuristic based initialLayout for the QuantumComputation. + This implementation is based on which qubits are controlled by whichqubits + * @details The function creates a map of each qubit to all the qubits it + controls. + * Based on the control to target relationship, a weight for each qubit is + calculated. + * The qubits are then sorted based on the weight in increasing order, + * each controlling qubit is placed after its targets + * @param QuantumComputation + * @return the qc::Permutation + */ + static qc::Permutation + createControlBasedPermutation(qc::QuantumComputation& qc); + + // Helper function for createControlBasedPermutation + + /** + * @brief recursively adjusts the weights of the qubits based on the control + * and target qubits + * @param map of qubit to weight, current set of target qubits, current + * control qubit, overall map of control qubit to target qubits, count of + * recoursive calls + * @return adjusted map of qubit to weight + */ + static std::map + adjustWeights(std::map qubitWeights, + const std::set& targets, Qubit ctrl, + const std::map>& controlToTargets, + int count); + + /** + * @brief creates a vector of all possible permutations of the initialLayout + used for first implementation of testing all Permutations and + drawing conclusions + * @param QuantumComputation + */ + static std::vector + createAllPermutations(qc::QuantumComputation& qc); + + /** + * @brief Helper function to compute how many permutations there are for a set + * number of qubits + * @param number of Bits + */ + static std::size_t factorial(std::size_t n); + +}; // class DDMinimizer +} // namespace qc diff --git a/src/DDMinimizer.cpp b/src/DDMinimizer.cpp new file mode 100644 index 00000000..b77ae4ff --- /dev/null +++ b/src/DDMinimizer.cpp @@ -0,0 +1,417 @@ +#include "DDMinimizer.hpp" + +#include "Definitions.hpp" +#include "circuit_optimizer/CircuitOptimizer.hpp" +#include "ir/Permutation.hpp" +#include "ir/QuantumComputation.hpp" +#include "ir/operations/Control.hpp" +#include "ir/operations/OpType.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace qc; +using namespace std; + +namespace qc { + +/*** + * Public Methods + ***/ + +void DDMinimizer::optimizeInputPermutation(qc::QuantumComputation& qc) { + // create, set and apply the heuristics based permutation + const qc::Permutation& perm = DDMinimizer::createGateBasedPermutation(qc); + qc.initialLayout = perm; + qc::CircuitOptimizer::elidePermutations(qc); +} + +qc::Permutation +DDMinimizer::createGateBasedPermutation(qc::QuantumComputation& qc) { + // create the data structure to store the indices in the pattern maps as well + // as the max indices of the ladders + + const std::size_t bits = qc.getNqubits(); + + // initialize the maps with the control and target qubits of the pattern + qc::DDMinimizer minimizer; + minimizer.initializeDataStructure(bits); + + // iterate over all the ops and mark the index of the found x-c pairs in the + // map. + int instructionIndex = 0; + bool found = false; + for (const auto& op : qc) { + if (!op->isStandardOperation() || op->getType() == Z) { + continue; + } + + for (const auto& control : op->getControls()) { + for (const auto& target : op->getTargets()) { + auto itxC = minimizer.xCMap.find({control.qubit, target}); + if (itxC != minimizer.xCMap.end()) { + itxC->second = instructionIndex; + found = true; + } + auto itcX = minimizer.cXMap.find({control.qubit, target}); + if (itcX != minimizer.cXMap.end()) { + itcX->second = instructionIndex; + found = true; + } + for (size_t i = 0; i < bits - 1; i++) { + auto itcL = minimizer.cLMap[i].find({control.qubit, target}); + if (itcL != minimizer.cLMap[i].end()) { + itcL->second = instructionIndex; + found = true; + } + auto itcH = minimizer.cHMap[i].find({control.qubit, target}); + if (itcH != minimizer.cHMap[i].end()) { + itcH->second = instructionIndex; + found = true; + } + auto itxL = minimizer.xLMap[i].find({control.qubit, target}); + if (itxL != minimizer.xLMap[i].end()) { + itxL->second = instructionIndex; + found = true; + } + auto itxH = minimizer.xHMap[i].find({control.qubit, target}); + if (itxH != minimizer.xHMap[i].end()) { + itxH->second = instructionIndex; + found = true; + } + } + } + } + instructionIndex++; + } + if (!found) { + + return qc.initialLayout; + } + + // iterate over all the maps and find the max index of the found x-c pairs + int cXIndex = 0; + int xCIndex = 0; + std::vector cLIndex(bits - 1, 0); + std::vector cHIndex(bits - 1, 0); + std::vector xLIndex(bits - 1, 0); + std::vector xHIndex(bits - 1, 0); + + cXIndex = DDMinimizer::findMaxIndex(minimizer.cXMap); + xCIndex = DDMinimizer::findMaxIndex(minimizer.xCMap); + for (size_t i = 0; i < bits - 1; i++) { + cLIndex[i] = DDMinimizer::findMaxIndex(minimizer.cLMap[i]); + cHIndex[i] = DDMinimizer::findMaxIndex(minimizer.cHMap[i]); + xLIndex[i] = DDMinimizer::findMaxIndex(minimizer.xLMap[i]); + xHIndex[i] = DDMinimizer::findMaxIndex(minimizer.xHMap[i]); + } + + // create the permutation based on the order of max index in the complete maps + std::vector layout(bits); + std::iota(layout.begin(), layout.end(), 0); + + const int prioCh = DDMinimizer::getLadderPosition(cHIndex, xCIndex); + const int prioXl = DDMinimizer::getLadderPosition(xLIndex, xCIndex); + const std::size_t stairsCh = DDMinimizer::getStairCount(cHIndex); + const std::size_t stairsXl = DDMinimizer::getStairCount(xLIndex); + const int prioCl = DDMinimizer::getLadderPosition(cLIndex, cXIndex); + const int prioXh = DDMinimizer::getLadderPosition(xHIndex, cXIndex); + const std::size_t stairsCl = DDMinimizer::getStairCount(cLIndex); + const std::size_t stairsXh = DDMinimizer::getStairCount(xHIndex); + + // complete case checkins and adjust the layout + // reverse of q | 0 1 2 3 turns to q | 0 1 2 3 + // l | 0 1 2 3 l | 3 2 1 0 + + if ((cXIndex != -1) && (xCIndex == -1 || cXIndex < xCIndex)) { + + if (prioCh == 0 && stairsCh > 0) { + std::reverse(layout.begin(), layout.end()); + layout = DDMinimizer::rotateLeft(layout, stairsCh); + } else if (prioXl == 0 && stairsXl > 0) { + std::reverse(layout.begin(), layout.end()); + layout = DDMinimizer::rotateRight(layout, stairsXl); + } else if (prioCh > 0 || prioXl > 0 || (prioCh == 0 && prioXl == 0)) { + std::reverse(layout.begin(), layout.end()); + } + } else if ((xCIndex != -1) && (cXIndex == -1 || cXIndex > xCIndex)) { + + if ((prioCl == 0 && DDMinimizer::isFullLadder(cLIndex)) || + (prioXh == 0 && DDMinimizer::isFullLadder(xHIndex))) { + std::reverse(layout.begin(), layout.end()); + } + + else if (prioCl == 0 && stairsCl > 0) { + layout = DDMinimizer::rotateRight(layout, stairsCl); + } else if (prioXh == 0 && stairsXh > 0) { + layout = DDMinimizer::rotateLeft(layout, stairsXh); + } + } else if ((DDMinimizer::isFullLadder(xHIndex) || + DDMinimizer::isFullLadder(cLIndex))) { + std::reverse(layout.begin(), layout.end()); + } else { + // in case no full pattern was identified, call fallback function to + // determine ordering based on singular controlled operations + return DDMinimizer::createControlBasedPermutation(qc); + } + + // transform layout into permutation + // Permutation is std::map + qc::Permutation perm; + for (qc::Qubit i = 0; i < bits; i++) { + perm[i] = layout[i]; + } + return perm; +} + +void DDMinimizer::initializeDataStructure(std::size_t bits) { + xCMap.clear(); + cXMap.clear(); + cLMap.resize(bits - 1); + cHMap.resize(bits - 1); + xLMap.resize(bits - 1); + xHMap.resize(bits - 1); + + const std::size_t max = bits - 1; + // create x-c ladder + for (size_t i = 0; i < max; i++) { + xCMap.insert({{i + 1, i}, -1}); + cXMap.insert({{i, i + 1}, -1}); + } + + // create c-l and x-l ladder + for (size_t i = 0; i < max; i++) { + for (size_t j = 0; j < bits; j++) { + if (i < j) { + xLMap[i].insert({{j, i}, -1}); + cLMap[i].insert({{i, j}, -1}); + } + } + } + + // create c-h and x-h ladder + for (size_t i = max; i > 0; i--) { + for (size_t j = 0; j < bits; j++) { + if (i > j) { + xHMap[bits - i - 1].insert({{j, i}, -1}); + cHMap[bits - i - 1].insert({{i, j}, -1}); + } + } + } +} + +int DDMinimizer::findMaxIndex( + const std::map, int>& map) { + int maxIndex = -1; // Initialize to the smallest possible value + + for (const auto& entry : map) { + // in case the index of -1 is encountered, this indicates that the overall + // pattern is not completely present in the circuit. Therefore we set the + // overall maxIndex to -1. With that we are able to filter out incomplete + // patterns as they do not impact the order of the permutation. + if (entry.second == -1) { + maxIndex = -1; + break; + } + if (entry.second > maxIndex) { + maxIndex = entry.second; + } + } + + return maxIndex; +} + +// Helper function to check if the vector of a ladder step is full, meaning each +// gate appeared in the circuit +bool DDMinimizer::isFullLadder(const std::vector& vec) { + return std::all_of(vec.begin(), vec.end(), + [](int value) { return value != -1; }); +} + +// Helper function to get the number of complete stairs in a ladder +std::size_t DDMinimizer::getStairCount(const std::vector& vec) { + std::size_t count = 0; + for (const int value : vec) { + if (value != -1) { + count++; + } else { + return count; + } + } + return count; +} + +// Helper function to get the position of a ladder in comparison to other steps +int DDMinimizer::getLadderPosition(const std::vector& vec, int ladder) { + int count = 0; + for (size_t i = 0; i < vec.size(); i++) { + const int value = vec[i]; + + if (i == vec.size() - 1 && count != 0) { + if (value != -1 && value < ladder) { + count++; + } + } + } + return count; +} + +// Helper functions to manipulate the Layout: rotate right and left + +std::vector DDMinimizer::rotateRight(std::vector layout, + std::size_t stairs) { + const std::size_t size = layout.size(); + std::vector rotatedLayout(size); + for (std::size_t r = 0; r < stairs; ++r) { + if (!layout.empty()) { + rotatedLayout[size - (r + 1)] = layout[r]; + } + } + const std::size_t left = size - stairs; + for (std::size_t r = 0; r < left; ++r) { + if (!layout.empty()) { + rotatedLayout[r] = layout[r + stairs]; + } + } + return rotatedLayout; +} + +std::vector DDMinimizer::rotateLeft(std::vector layout, + std::size_t stairs) { + const std::size_t size = layout.size(); + std::vector rotatedLayout(size); + for (std::size_t r = 0; r < stairs; ++r) { + if (!layout.empty()) { + rotatedLayout[r] = layout[size - (r + 1)]; + } + } + const std::size_t left = size - stairs; + for (std::size_t r = 0; r < left; ++r) { + if (!layout.empty()) { + rotatedLayout[r + stairs] = layout[r]; + } + } + return rotatedLayout; +} + +// Fallback function to create a control based permutation if no pattern is +// found in the controlled gates +qc::Permutation +DDMinimizer::createControlBasedPermutation(qc::QuantumComputation& qc) { + // create and fill a map of each qubit to all the qubits it controls + std::map> controlToTargets; + + // iterate over all the ops to mark which qubits are controlled by which + // qubits + for (const auto& op : qc) { + if (!op->isStandardOperation()) { + continue; + } + const Controls controls = op->getControls(); + const std::set& targets = {op->getTargets().begin(), + op->getTargets().end()}; + + for (const auto& control : controls) { + if (controlToTargets.find(control.qubit) == controlToTargets.end()) { + // If the control does not exist in the map, add it with the current set + // of targetInts + controlToTargets[control.qubit] = targets; + } else { + // If the control exists, insert the new targets into the existing set + controlToTargets[control.qubit].insert(targets.begin(), targets.end()); + } + } + } + + if (controlToTargets.empty()) { + return qc.initialLayout; + } + + // create a weights map for the qubits to evaluate the order of the qubits + const std::size_t bits = qc.getNqubits(); + + std::vector qubit(bits); + std::vector layer(bits); + std::map qubitWeights; + for (Qubit i = 0; i < bits; ++i) { + layer[i] = i; + qubit[i] = i; + qubitWeights.insert({i, 1}); + } + + std::set encounteredTargets; + int weight = 1; + // compute and anjust the weight of the controlling qubit based on all control + // to target relations + for (const auto& pair : controlToTargets) { + // if the current control qubit is already encountered as a previous target, + // adjust the weights + if (encounteredTargets.find(pair.first) != encounteredTargets.end()) { + qubitWeights = DDMinimizer::adjustWeights( + qubitWeights, encounteredTargets, pair.first, controlToTargets, 1); + } + // get a base line weight for the control qubit as the previous one + weight = qubitWeights[pair.first]; + + // iterate over all the targets that are controlled by the current qubit in + // current item of the control to target map + for (const auto& target : pair.second) { + // save all the encountered targets + encounteredTargets.insert(target); + // compute the new weight of the control qubit based on all its targets + // and the base line + weight = std::max(qubitWeights[target], weight + 1); + } + weight++; + qubitWeights[pair.first] = weight; + } + + // sort the layout based on the weights of the qubits + sort(layer.begin(), layer.end(), + [&qubitWeights](const Qubit& a, const Qubit& b) { + auto weightA = qubitWeights.find(a) != qubitWeights.end() + ? qubitWeights.at(a) + : 0; + auto weightB = qubitWeights.find(b) != qubitWeights.end() + ? qubitWeights.at(b) + : 0; + return weightA < weightB; + }); + + // transform the layout into a permutationq + qc::Permutation perm; + for (std::size_t m = 0; m < bits; m++) { + perm[qubit[m]] = layer[m]; + } + return perm; +} + +// Helper function to adjust the weights of the qubits when a controlling qubit +// was previously a target +std::map DDMinimizer::adjustWeights( + std::map qubitWeights, const std::set& targets, + Qubit ctrl, const std::map>& controlToTargets, + int count) { + // avoid infinite loop + if (count == static_cast(controlToTargets.size())) { + return qubitWeights; + } + // recursively increase all the weights of the control qubits where the + // current controlling one was a target + for (const auto& controlPair : controlToTargets) { + if (controlPair.second.find(ctrl) != controlPair.second.end()) { + const Qubit control = controlPair.first; + qubitWeights[controlPair.first]++; + qubitWeights = DDMinimizer::adjustWeights(qubitWeights, targets, control, + controlToTargets, count + 1); + } + } + return qubitWeights; +} + +}; // namespace qc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 09b67d57..e9220447 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,6 +10,7 @@ package_add_test( test_det_noise_sim.cpp test_unitary_sim.cpp test_path_sim.cpp - test_output_ddvis.cpp) + test_output_ddvis.cpp + test_reorder_without_reordering.cpp) target_link_libraries(mqt-ddsim-test PRIVATE MQT::CoreAlgorithms) diff --git a/test/test_reorder_without_reordering.cpp b/test/test_reorder_without_reordering.cpp new file mode 100644 index 00000000..3e30bba2 --- /dev/null +++ b/test/test_reorder_without_reordering.cpp @@ -0,0 +1,164 @@ +#include "DDMinimizer.hpp" +#include "ir/Permutation.hpp" +#include "ir/QuantumComputation.hpp" + +#include +#include // For std::string + +using namespace qc; +using namespace std; + +TEST(ReorderWithoutReorderingTest, reorderXc) { + // control -> target + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[1],q[0];" + "cx q[2],q[1];" + "cx q[3],q[2];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 0}, {1, 1}, {2, 2}, {3, 3}}}; + + EXPECT_EQ(expectedPerm, perm); +} + +TEST(ReorderWithoutReorderingTest, reorderCx) { + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[0],q[1];" + "cx q[1],q[2];" + "cx q[2],q[3];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 3}, {1, 2}, {2, 1}, {3, 0}}}; + + EXPECT_EQ(expectedPerm, perm); +} + +TEST(ReorderWithoutReorderingTest, reorderXccl) { + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[1],q[0];" + "cx q[2],q[1];" + "cx q[3],q[2];" + "cx q[0],q[1];" + "cx q[0],q[2];" + "cx q[0],q[3];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 1}, {1, 2}, {2, 3}, {3, 0}}}; + + EXPECT_EQ(expectedPerm, perm); +} + +// failing +TEST(ReorderWithoutReorderingTest, reorderXcxh) { + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[1],q[0];" + "cx q[2],q[1];" + "cx q[3],q[2];" + "cx q[0],q[3];" + "cx q[1],q[3];" + "cx q[2],q[3];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 3}, {1, 0}, {2, 1}, {3, 2}}}; + + EXPECT_EQ(expectedPerm, perm); +} + +TEST(ReorderWithoutReorderingTest, reorderCxch) { + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[0],q[1];" + "cx q[1],q[2];" + "cx q[2],q[3];" + "cx q[1],q[0];" + "cx q[2],q[0];" + "cx q[3],q[0];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 2}, {1, 1}, {2, 0}, {3, 3}}}; + + EXPECT_EQ(expectedPerm, perm); +} + +// failing +TEST(ReorderWithoutReorderingTest, reorderCxxl) { + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[0],q[1];" + "cx q[1],q[2];" + "cx q[2],q[3];" + "cx q[3],q[0];" + "cx q[3],q[1];" + "cx q[3],q[2];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 0}, {1, 3}, {2, 2}, {3, 1}}}; + + EXPECT_EQ(expectedPerm, perm); +} + +TEST(ReorderWithoutReorderingTest, reorderInterlacedQubits) { + const std::string testfile = "OPENQASM 2.0;" + "include \"qelib1.inc\";" + "qreg q[4];" + "creg meas[4];" + "h q;" + "cx q[0],q[1];" + "cx q[1],q[3];" + "cx q[3],q[2];" + "barrier q;" + "measure q -> meas;"; + + auto qc = QuantumComputation::fromQASM(testfile); + const qc::Permutation perm = DDMinimizer::createGateBasedPermutation(qc); + + const Permutation expectedPerm = {{{0, 2}, {1, 3}, {2, 1}, {3, 0}}}; + + EXPECT_EQ(expectedPerm, perm); +}