From 8c85f9b701405c798ffc9d807a77433c3ea3e434 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 10 Mar 2023 16:14:21 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20add=20two-qubit=20gate=20matrix?= =?UTF-8?q?=20definitions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/dd/GateMatrixDefinitions.hpp | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/include/dd/GateMatrixDefinitions.hpp b/include/dd/GateMatrixDefinitions.hpp index 4582aa60..59c7d3f7 100644 --- a/include/dd/GateMatrixDefinitions.hpp +++ b/include/dd/GateMatrixDefinitions.hpp @@ -83,6 +83,106 @@ namespace dd { complex_zero, {std::cos(lambda / 2.), std::sin(lambda / 2.)}}}; } + + using TwoQubitGateMatrix = std::array, NEDGE>; + constexpr TwoQubitGateMatrix CXmat{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, complex_one, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_one}, + {complex_zero, complex_zero, complex_one, complex_zero}}}; + + constexpr TwoQubitGateMatrix CZmat{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, complex_one, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_one, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_mone}}}; + + constexpr TwoQubitGateMatrix SWAPmat{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_one, complex_zero}, + {complex_zero, complex_one, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_one}}}; + + constexpr TwoQubitGateMatrix iSWAPmat{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_i, complex_zero}, + {complex_zero, complex_i, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_one}}}; + + constexpr TwoQubitGateMatrix iSWAPinvmat{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_mi, complex_zero}, + {complex_zero, complex_mi, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_one}}}; + + constexpr TwoQubitGateMatrix ECRmat{{{complex_zero, complex_zero, complex_SQRT2_2, complex_iSQRT2_2}, + {complex_zero, complex_zero, complex_iSQRT2_2, complex_SQRT2_2}, + {complex_SQRT2_2, complex_miSQRT2_2, complex_zero, complex_zero}, + {complex_miSQRT2_2, complex_SQRT2_2, complex_zero, complex_zero}}}; + + constexpr TwoQubitGateMatrix DCXmat{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_one}, + {complex_zero, complex_one, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_one, complex_zero}}}; + + inline TwoQubitGateMatrix RXXmat(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return TwoQubitGateMatrix{{{ComplexValue{cosTheta, 0.}, complex_zero, complex_zero, ComplexValue{0., -sinTheta}}, + {complex_zero, ComplexValue{cosTheta, 0.}, ComplexValue{0., -sinTheta}, complex_zero}, + {complex_zero, ComplexValue{0., -sinTheta}, ComplexValue{cosTheta, 0.}, complex_zero}, + {ComplexValue{0., -sinTheta}, complex_zero, complex_zero, ComplexValue{cosTheta, 0.}}}}; + } + + inline TwoQubitGateMatrix RYYmat(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return TwoQubitGateMatrix{{{ComplexValue{cosTheta, 0.}, complex_zero, complex_zero, ComplexValue{0., sinTheta}}, + {complex_zero, ComplexValue{cosTheta, 0.}, ComplexValue{0., -sinTheta}, complex_zero}, + {complex_zero, ComplexValue{0., -sinTheta}, ComplexValue{cosTheta, 0.}, complex_zero}, + {ComplexValue{0., sinTheta}, complex_zero, complex_zero, ComplexValue{cosTheta, 0.}}}}; + } + + inline TwoQubitGateMatrix RZZmat(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return TwoQubitGateMatrix{{{ComplexValue{cosTheta, -sinTheta}, complex_zero, complex_zero, complex_zero}, + {complex_zero, ComplexValue{cosTheta, sinTheta}, complex_zero, complex_zero}, + {complex_zero, complex_zero, ComplexValue{cosTheta, sinTheta}, complex_zero}, + {complex_zero, complex_zero, complex_zero, ComplexValue{cosTheta, -sinTheta}}}}; + } + + inline TwoQubitGateMatrix RZXmat(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return TwoQubitGateMatrix{{{ComplexValue{cosTheta, 0.}, ComplexValue{0., -sinTheta}, complex_zero, complex_zero}, + {ComplexValue{0., -sinTheta}, ComplexValue{cosTheta, 0.}, complex_zero, complex_zero}, + {complex_zero, complex_zero, ComplexValue{cosTheta, 0.}, ComplexValue{0., sinTheta}}, + {complex_zero, complex_zero, ComplexValue{0., sinTheta}, ComplexValue{cosTheta, 0.}}}}; + } + + inline TwoQubitGateMatrix XXMinusYYmat(const fp theta, const fp beta = 0.) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + const auto cosBeta = std::cos(beta); + const auto sinBeta = std::sin(beta); + + return TwoQubitGateMatrix{{{ComplexValue{cosTheta, 0.}, complex_zero, complex_zero, ComplexValue{sinBeta * sinTheta, -cosBeta * sinTheta}}, + {complex_zero, complex_one, complex_zero, complex_zero}, + {complex_zero, complex_zero, complex_one, complex_zero}, + {ComplexValue{-sinBeta * sinTheta, -cosBeta * sinTheta}, complex_zero, complex_zero, ComplexValue{cosTheta, 0.}}}}; + } + + inline TwoQubitGateMatrix XXPlusYYmat(const fp theta, const fp beta = 0.) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + const auto cosBeta = std::cos(beta); + const auto sinBeta = std::sin(beta); + + return TwoQubitGateMatrix{{{complex_one, complex_zero, complex_zero, complex_zero}, + {complex_zero, ComplexValue{cosTheta, 0.}, ComplexValue{sinBeta * sinTheta, -cosBeta * sinTheta}, complex_zero}, + {complex_zero, ComplexValue{-sinBeta * sinTheta, -cosBeta * sinTheta}, ComplexValue{cosTheta, 0.}, complex_zero}, + {complex_zero, complex_zero, complex_zero, complex_one}}}; + } // NOLINTEND(readability-identifier-naming) } // namespace dd #endif //DD_PACKAGE_GATEMATRIXDEFINITIONS_H From 7d5514dd80ae3ebd88de20a80430072b3af512a0 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 10 Mar 2023 16:15:38 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20add=20method=20for=20creating?= =?UTF-8?q?=20two-qubit=20gate=20DD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/dd/Package.hpp | 93 +++++++++++++++++++++++++++++++++++++++++- test/test_package.cpp | 2 +- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index 251d9960..ab8d23a4 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -538,7 +538,98 @@ namespace dd { return e; } - mEdge makeSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + /** + Creates the DD for a two-qubit gate + @param mat Matrix representation of the gate + @param n Number of qubits in the circuit + @param target0 First target qubit + @param target1 Second target qubit + @param start Start index for the DD + @return DD representing the gate + @throws std::runtime_error if the number of qubits is larger than the package configuration + **/ + mEdge makeTwoQubitGateDD(const std::array, NEDGE>& mat, const QubitCount n, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + // sanity check + if (n + start > nqubits) { + throw std::runtime_error{"Requested gate with " + + std::to_string(n + start) + + " qubits, but current package configuration only supports up to " + + std::to_string(nqubits) + + " qubits. Please allocate a larger package instance."}; + } + + // create terminal edge matrix + std::array, NEDGE> em{}; + for (auto i1 = 0U; i1 < NEDGE; i1++) { + const auto& matRow = mat.at(i1); + auto& emRow = em.at(i1); + for (auto i2 = 0U; i2 < NEDGE; i2++) { + const auto& matEntry = matRow.at(i2); + auto& emEntry = emRow.at(i2); + // NOLINTNEXTLINE(clang-diagnostic-float-equal) it has to be really zero + if (matEntry.r == 0 && matEntry.i == 0) { + emEntry = mEdge::zero; + } else { + emEntry = mEdge::terminal(cn.lookup(matEntry)); + } + } + } + + // process lines below smaller target (by creating identity structures) + auto z = static_cast(start); + const auto smallerTarget = std::min(target0, target1); + for (; z < smallerTarget; ++z) { + for (auto& row: em) { + for (auto& entry: row) { + entry = makeDDNode(z, std::array{entry, mEdge::zero, mEdge::zero, entry}); + } + } + } + + // process the smaller target by taking the 16 submatrices and appropriately combining them into four DDs. + std::array em0{}; + for (std::size_t row = 0; row < RADIX; ++row) { + for (std::size_t col = 0; col < RADIX; ++col) { + std::array local{}; + if (target0 > target1) { + for (std::size_t i = 0; i < RADIX; ++i) { + for (std::size_t j = 0; j < RADIX; ++j) { + local.at(i * RADIX + j) = em.at(row * RADIX + i).at(col * RADIX + j); + } + } + } else { + for (std::size_t i = 0; i < RADIX; ++i) { + for (std::size_t j = 0; j < RADIX; ++j) { + local.at(i * RADIX + j) = em.at(i * RADIX + row).at(j * RADIX + col); + } + } + } + em0.at(row * RADIX + col) = makeDDNode(z, local); + } + } + + // process lines between the two targets (by creating identity structures) + for (++z; z < std::max(target0, target1); ++z) { + for (auto& entry: em0) { + entry = makeDDNode(z, std::array{entry, mEdge::zero, mEdge::zero, entry}); + } + } + + // process the larger target by combining the four DDs from the smaller target + auto e = makeDDNode(z, em0); + + // process lines above the larger target (by creating identity structures) + for (++z; z < static_cast(n + start); ++z) { + e = makeDDNode(z, std::array{e, mEdge::zero, mEdge::zero, e}); + } + + return e; + } + + mEdge makeSWAPDD(const QubitCount n, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + return makeTwoQubitGateDD(SWAPmat, n, target0, target1, start); + } + mEdge makeSWAPDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { auto c = controls; c.insert(Control{target0}); mEdge e = makeGateDD(Xmat, n, c, target1, start); diff --git a/test/test_package.cpp b/test/test_package.cpp index f96ff60e..bd657e15 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -91,7 +91,7 @@ TEST(DDPackageTest, QFTState) { auto h1Gate = dd->makeGateDD(dd::Hmat, 3, 1); auto s1Gate = dd->makeGateDD(dd::Smat, 3, 2_pc, 1); auto h2Gate = dd->makeGateDD(dd::Hmat, 3, 2); - auto swapGate = dd->makeSWAPDD(3, {}, 0, 2); + auto swapGate = dd->makeSWAPDD(3, dd::Controls{}, 0, 2); auto qftOp = dd->multiply(s0Gate, h0Gate); qftOp = dd->multiply(t0Gate, qftOp); From 49cb04f344d37e7ed341b7343b8fa970196b0287 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 10 Mar 2023 16:18:12 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20add=20new=20methods=20for=20DD?= =?UTF-8?q?=20creation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit these methods are inspired by the definitions in Qiskit and are here for two reasons: - they provide ways to create controlled versions of the new gates - they provide a sanity check for the direct DD implementations --- include/dd/Package.hpp | 134 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/include/dd/Package.hpp b/include/dd/Package.hpp index ab8d23a4..81a29d47 100644 --- a/include/dd/Package.hpp +++ b/include/dd/Package.hpp @@ -639,7 +639,7 @@ namespace dd { return e; } - mEdge makePeresDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + mEdge makePeresDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { auto c = controls; c.insert(Control{target1}); mEdge e = makeGateDD(Xmat, n, c, target0, start); @@ -647,7 +647,7 @@ namespace dd { return e; } - mEdge makePeresdagDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + mEdge makePeresdagDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { mEdge e = makeGateDD(Xmat, n, controls, target1, start); auto c = controls; c.insert(Control{target1}); @@ -655,7 +655,10 @@ namespace dd { return e; } - mEdge makeiSWAPDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + mEdge makeiSWAPDD(const QubitCount n, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + return makeTwoQubitGateDD(iSWAPmat, n, target0, target1, start); + } + mEdge makeiSWAPDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { mEdge e = makeGateDD(Smat, n, controls, target1, start); // S q[1] e = multiply(e, makeGateDD(Smat, n, controls, target0, start)); // S q[0] e = multiply(e, makeGateDD(Hmat, n, controls, target0, start)); // H q[0] @@ -669,7 +672,10 @@ namespace dd { return e; } - mEdge makeiSWAPinvDD(QubitCount n, const Controls& controls, Qubit target0, Qubit target1, std::size_t start = 0) { + mEdge makeiSWAPinvDD(const QubitCount n, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + return makeTwoQubitGateDD(iSWAPinvmat, n, target0, target1, start); + } + mEdge makeiSWAPinvDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { mEdge e = makeGateDD(Hmat, n, controls, target1, start); // H q[1] auto c = controls; c.insert(Control{target1}); @@ -683,6 +689,126 @@ namespace dd { return e; } + mEdge makeDCXDD(const QubitCount n, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + return makeTwoQubitGateDD(DCXmat, n, target0, target1, start); + } + mEdge makeDCXDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + auto c = controls; + c.insert(Control{target0}); + mEdge e = makeGateDD(Xmat, n, c, target1, start); + c.erase(Control{target0}); + c.insert(Control{target1}); + e = multiply(e, makeGateDD(Xmat, n, c, target0, start)); + return e; + } + + mEdge makeRZZDD(const QubitCount n, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + return makeTwoQubitGateDD(RZZmat(theta), n, target0, target1, start); + } + mEdge makeRZZDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + auto c = controls; + c.insert(Control{target0}); + auto e = makeGateDD(Xmat, n, c, target1, start); + c.erase(Control{target0}); + e = multiply(e, makeGateDD(RZmat(theta), n, c, target1, start)); + c.insert(Control{target0}); + e = multiply(e, makeGateDD(Xmat, n, c, target1, start)); + return e; + } + + mEdge makeRYYDD(const QubitCount n, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + return makeTwoQubitGateDD(RYYmat(theta), n, target0, target1, start); + } + mEdge makeRYYDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + // no controls are necessary on the RX gates since they cancel if the controls are 0. + auto e = makeGateDD(RXmat(PI_2), n, Controls{}, target0, start); + e = multiply(e, makeGateDD(RXmat(PI_2), n, Controls{}, target1, start)); + e = multiply(e, makeRZZDD(n, controls, target0, target1, theta, start)); + e = multiply(e, makeGateDD(RXmat(-PI_2), n, Controls{}, target1, start)); + e = multiply(e, makeGateDD(RXmat(-PI_2), n, Controls{}, target0, start)); + return e; + } + + mEdge makeRXXDD(const QubitCount n, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + return makeTwoQubitGateDD(RXXmat(theta), n, target0, target1, start); + } + mEdge makeRXXDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + // no controls are necessary on the H gates since they cancel if the controls are 0. + auto e = makeGateDD(Hmat, n, Controls{}, target0, start); + e = multiply(e, makeGateDD(Hmat, n, Controls{}, target1, start)); + e = multiply(e, makeRZZDD(n, controls, target0, target1, theta, start)); + e = multiply(e, makeGateDD(Hmat, n, Controls{}, target1, start)); + e = multiply(e, makeGateDD(Hmat, n, Controls{}, target0, start)); + return e; + } + + mEdge makeRZXDD(const QubitCount n, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + return makeTwoQubitGateDD(RZXmat(theta), n, target0, target1, start); + } + mEdge makeRZXDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const fp theta, const std::size_t start = 0) { + // no controls are necessary on the H gates since they cancel if the controls are 0. + auto e = makeGateDD(Hmat, n, Controls{}, target1, start); + e = multiply(e, makeRZZDD(n, controls, target0, target1, theta, start)); + e = multiply(e, makeGateDD(Hmat, n, Controls{}, target1, start)); + return e; + } + + mEdge makeECRDD(const QubitCount n, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + return makeTwoQubitGateDD(ECRmat, n, target0, target1, start); + } + mEdge makeECRDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const std::size_t start = 0) { + auto e = makeRZXDD(n, controls, target0, target1, -PI_4, start); + e = multiply(e, makeGateDD(Xmat, n, controls, target0, start)); + e = multiply(e, makeRZXDD(n, controls, target0, target1, PI_4, start)); + return e; + } + + mEdge makeXXMinusYYDD(const QubitCount n, const Qubit target0, const Qubit target1, const fp theta, const fp beta = 0., const std::size_t start = 0) { + return makeTwoQubitGateDD(XXMinusYYmat(theta, beta), n, target0, target1, start); + } + mEdge makeXXMinusYYDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const fp theta, const fp beta = 0., const std::size_t start = 0) { + auto e = makeGateDD(RZmat(-beta), n, Controls{}, target1, start); + e = multiply(e, makeGateDD(RZmat(-PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(SXmat, n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(RZmat(PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(Smat, n, Controls{}, target1, start)); + e = multiply(e, makeGateDD(Xmat, n, Control{target0}, target1, start)); + // only the following two gates need to be controlled by the controls since the other gates cancel if the controls are 0. + e = multiply(e, makeGateDD(RYmat(-theta / 2.), n, controls, target0, start)); + e = multiply(e, makeGateDD(RYmat(theta / 2.), n, controls, target1, start)); + + e = multiply(e, makeGateDD(Xmat, n, Control{target0}, target1, start)); + e = multiply(e, makeGateDD(Sdagmat, n, Controls{}, target1, start)); + e = multiply(e, makeGateDD(RZmat(-PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(SXdagmat, n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(RZmat(PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(RZmat(beta), n, Controls{}, target1, start)); + return e; + } + + mEdge makeXXPlusYYDD(const QubitCount n, const Qubit target0, const Qubit target1, const fp theta, const fp beta = 0., const std::size_t start = 0) { + return makeTwoQubitGateDD(XXPlusYYmat(theta, beta), n, target0, target1, start); + } + mEdge makeXXPlusYYDD(const QubitCount n, const Controls& controls, const Qubit target0, const Qubit target1, const fp theta, const fp beta = 0., const std::size_t start = 0) { + auto e = makeGateDD(RZmat(beta), n, Controls{}, target1, start); + e = multiply(e, makeGateDD(RZmat(-PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(SXmat, n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(RZmat(PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(Smat, n, Controls{}, target1, start)); + e = multiply(e, makeGateDD(Xmat, n, Control{target0}, target1, start)); + // only the following two gates need to be controlled by the controls since the other gates cancel if the controls are 0. + e = multiply(e, makeGateDD(RYmat(theta / 2.), n, controls, target0, start)); + e = multiply(e, makeGateDD(RYmat(theta / 2.), n, controls, target1, start)); + + e = multiply(e, makeGateDD(Xmat, n, Control{target0}, target1, start)); + e = multiply(e, makeGateDD(Sdagmat, n, Controls{}, target1, start)); + e = multiply(e, makeGateDD(RZmat(-PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(SXdagmat, n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(RZmat(PI_2), n, Controls{}, target0, start)); + e = multiply(e, makeGateDD(RZmat(-beta), n, Controls{}, target1, start)); + return e; + } + private: // check whether node represents a symmetric matrix or the identity void checkSpecialMatrices(mNode* p) { From ed9e00687f9f0058e48dd19889ecafbd9180b8f8 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Fri, 10 Mar 2023 16:21:13 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=85=20add=20tests=20for=20two-qubit?= =?UTF-8?q?=20gate=20DD=20creation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/test_package.cpp | 268 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/test/test_package.cpp b/test/test_package.cpp index bd657e15..16339c93 100644 --- a/test/test_package.cpp +++ b/test/test_package.cpp @@ -1518,3 +1518,271 @@ TEST(DDPackageTest, DDFromSingleElementMatrix) { EXPECT_EQ(dd->makeDDFromMatrix(inputMatrix), dd::mEdge::one); } + +TEST(DDPackageTest, TwoQubitControlledGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto gateMatrices = std::vector{ + std::pair{dd::Xmat, dd::CXmat}, + std::pair{dd::Zmat, dd::CZmat}}; + + // For every combination of control and target, test that the DD created by makeTwoQubitGateDD is equal to the DD created by makeGateDD. + // This should cover every scenario of the makeTwoQubitGateDD function. + for (const auto& [gateMatrix, controlledGateMatrix]: gateMatrices) { + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + const auto controlledGateDD = dd->makeTwoQubitGateDD(controlledGateMatrix, nrQubits, control, target); + const auto gateDD = dd->makeGateDD(gateMatrix, nrQubits, dd::Controls{dd::Control{control}}, target); + EXPECT_EQ(controlledGateDD, gateDD); + } + } + } +} + +TEST(DDPackageTest, SWAPGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + const auto swapGateDD = dd->makeSWAPDD(nrQubits, control, target); + const auto gateDD = dd->makeSWAPDD(nrQubits, dd::Controls{}, control, target); + EXPECT_EQ(swapGateDD, gateDD); + } + } +} + +TEST(DDPackageTest, iSWAPGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + const auto iswapGateDD = dd->makeiSWAPDD(nrQubits, control, target); + const auto gateDD = dd->makeiSWAPDD(nrQubits, dd::Controls{}, control, target); + EXPECT_EQ(iswapGateDD, gateDD); + + const auto iswapInvGateDD = dd->makeiSWAPinvDD(nrQubits, control, target); + const auto gateInvDD = dd->makeiSWAPinvDD(nrQubits, dd::Controls{}, control, target); + EXPECT_EQ(iswapInvGateDD, gateInvDD); + } + } +} + +TEST(DDPackageTest, DCXGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + const auto dcxGateDD = dd->makeDCXDD(nrQubits, control, target); + const auto gateDD = dd->makeDCXDD(nrQubits, dd::Controls{}, control, target); + EXPECT_EQ(dcxGateDD, gateDD); + } + } +} + +TEST(DDPackageTest, RZZGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto params = {0., dd::PI_2, dd::PI, 2 * dd::PI}; + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + for (const auto& param: params) { + const auto rzzGateDD = dd->makeRZZDD(nrQubits, control, target, param); + const auto gateDD = dd->makeRZZDD(nrQubits, dd::Controls{}, control, target, param); + EXPECT_EQ(rzzGateDD, gateDD); + } + } + } + + auto identity = dd->makeIdent(2); + auto rzzZero = dd->makeRZZDD(2, 0, 1, 0.); + EXPECT_EQ(rzzZero, identity); + + auto rzzTwoPi = dd->makeRZZDD(2, 0, 1, 2 * dd::PI); + EXPECT_EQ(rzzTwoPi.p, identity.p); + EXPECT_EQ(dd::ComplexTable<>::Entry::val(rzzTwoPi.w.r), -1.); + + auto rzzPi = dd->makeRZZDD(2, 0, 1, dd::PI); + auto zz = dd->makeGateDD(dd::Zmat, 2, dd::Controls{}, 0); + zz = dd->multiply(zz, dd->makeGateDD(dd::Zmat, 2, dd::Controls{}, 1)); + EXPECT_EQ(rzzPi.p, zz.p); +} + +TEST(DDPackageTest, RYYGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto params = {0., dd::PI_2, dd::PI}; + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + for (const auto& param: params) { + const auto ryyGateDD = dd->makeRYYDD(nrQubits, control, target, param); + const auto gateDD = dd->makeRYYDD(nrQubits, dd::Controls{}, control, target, param); + EXPECT_EQ(ryyGateDD, gateDD); + } + } + } + + auto identity = dd->makeIdent(2); + auto ryyZero = dd->makeRYYDD(2, 0, 1, 0.); + EXPECT_EQ(ryyZero, identity); + + auto ryyPi = dd->makeRYYDD(2, 0, 1, dd::PI); + auto yy = dd->makeGateDD(dd::Ymat, 2, dd::Controls{}, 0); + yy = dd->multiply(yy, dd->makeGateDD(dd::Ymat, 2, dd::Controls{}, 1)); + EXPECT_EQ(ryyPi.p, yy.p); +} + +TEST(DDPackageTest, RXXGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto params = {0., dd::PI_2, dd::PI}; + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + for (const auto& param: params) { + const auto rxxGateDD = dd->makeRXXDD(nrQubits, control, target, param); + const auto gateDD = dd->makeRXXDD(nrQubits, dd::Controls{}, control, target, param); + EXPECT_EQ(rxxGateDD, gateDD); + } + } + } + + auto identity = dd->makeIdent(2); + auto rxxZero = dd->makeRXXDD(2, 0, 1, 0.); + EXPECT_EQ(rxxZero, identity); + + auto rxxPi = dd->makeRXXDD(2, 0, 1, dd::PI); + auto xx = dd->makeGateDD(dd::Xmat, 2, dd::Controls{}, 0); + xx = dd->multiply(xx, dd->makeGateDD(dd::Xmat, 2, dd::Controls{}, 1)); + EXPECT_EQ(rxxPi.p, xx.p); +} + +TEST(DDPackageTest, RZXGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto params = {0., dd::PI_2, dd::PI}; + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + for (const auto& param: params) { + const auto rzxGateDD = dd->makeRZXDD(nrQubits, control, target, param); + const auto gateDD = dd->makeRZXDD(nrQubits, dd::Controls{}, control, target, param); + EXPECT_EQ(rzxGateDD, gateDD); + } + } + } + + auto identity = dd->makeIdent(2); + auto rzxZero = dd->makeRZXDD(2, 0, 1, 0.); + EXPECT_EQ(rzxZero, identity); + + auto rzxPi = dd->makeRZXDD(2, 0, 1, dd::PI); + auto zx = dd->makeGateDD(dd::Zmat, 2, dd::Controls{}, 0); + zx = dd->multiply(zx, dd->makeGateDD(dd::Xmat, 2, dd::Controls{}, 1)); + EXPECT_EQ(rzxPi.p, zx.p); +} + +TEST(DDPackageTest, ECRGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + + const auto ecrGateDD = dd->makeECRDD(nrQubits, control, target); + const auto gateDD = dd->makeECRDD(nrQubits, dd::Controls{}, control, target); + EXPECT_EQ(ecrGateDD, gateDD); + } + } +} + +TEST(DDPackageTest, XXMinusYYGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto thetaAngles = {0., dd::PI_2, dd::PI}; + const auto betaAngles = {0., dd::PI_2, dd::PI}; + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + + for (const auto& theta: thetaAngles) { + for (const auto& beta: betaAngles) { + const auto xxMinusYYGateDD = dd->makeXXMinusYYDD(nrQubits, control, target, theta, beta); + const auto gateDD = dd->makeXXMinusYYDD(nrQubits, dd::Controls{}, control, target, theta, beta); + EXPECT_EQ(xxMinusYYGateDD, gateDD); + } + } + } + } +} + +TEST(DDPackageTest, XXPlusYYGateDDConstruction) { + const dd::QubitCount nrQubits = 5; + const auto dd = std::make_unique>(nrQubits); + + const auto thetaAngles = {0., dd::PI_2, dd::PI}; + const auto betaAngles = {0., dd::PI_2, dd::PI}; + + for (dd::Qubit control = 0; control < nrQubits; ++control) { + for (dd::Qubit target = 0; target < nrQubits; ++target) { + if (control == target) { + continue; + } + + for (const auto& theta: thetaAngles) { + for (const auto& beta: betaAngles) { + const auto xxPlusYYGateDD = dd->makeXXPlusYYDD(nrQubits, control, target, theta, beta); + const auto gateDD = dd->makeXXPlusYYDD(nrQubits, dd::Controls{}, control, target, theta, beta); + EXPECT_EQ(xxPlusYYGateDD, gateDD); + } + } + } + } +} + +TEST(DDPackageTest, TwoQubitGateCreationFailure) { + const dd::QubitCount nrQubits = 1; + const auto dd = std::make_unique>(nrQubits); + + EXPECT_THROW(dd->makeTwoQubitGateDD(dd::CXmat, 2, 0, 1), std::runtime_error); +}