diff --git a/.agent/plans/dd-abi.md b/.agent/plans/dd-abi.md new file mode 100644 index 0000000000..b29b0274b4 --- /dev/null +++ b/.agent/plans/dd-abi.md @@ -0,0 +1,46 @@ +# Concrete DD operations + +Status: complete. + +## Outcome and scope + +Node-specific DD operations are concrete overloads in `dd/Edge.hpp` and +`dd/CachedEdge.hpp`. C++ callers use `dd::getVector(edge)`, +`dd::getMatrix(edge, numQubits)`, and the corresponding free operations. Generic +`Edge` and `CachedEdge` handles retain their layouts and +node-independent operations. Python methods and array ownership stay the same. +The migration covers Package dispatch, DD and MLIR tests, DDSIM, bindings, and +`docs/cpp_api.md`. + +## Decisions + +Constrained exported template members have different symbol names across +supported GCC and Clang versions. Concrete overloads remove that constraint +mangling boundary without compatibility wrappers or new traits. Numerical +algorithms and error handling stay unchanged. File-local traversal helpers pass +the callback by reference; the public matrix traversal owns one callback copy. +Cached-edge class instantiations are unnecessary because all remaining members +are defined in the header. Out-of-line edge and hash instantiations remain. + +## Validation + +- Native release build and `ctest --preset release --parallel 4`: 3,950 tests + passed; the superconducting device's unsupported job-ID property caused one + expected skip. This includes the DD, MLIR, QIR, and DDSIM consumers. The build + used GCC 13 and the assertion-free LLVM/MLIR 23.1.2 SDK without IPO. +- `uv run --no-sync pytest test/python -n4`: all 1,920 tests passed with both + bundled devices enabled. `uvx nox -s stubs` produced no tracked stub changes. +- `uvx nox -s lint` and `uvx nox -s cpp-lint`: passed. C++ lint inspected every + changed source file, including the full contents outside changed lines. +- Doxygen 1.17 built the native reference with the repository's Doxyfile and + QDMI inventory configuration from `docs/_ext/cpp_api.py`. The C++ guide's GHZ + example compiled and produced the expected amplitudes. +- Unoptimized GCC 13 and Clang 23 local consumers compiled and ran. Compile-time + probes accepted the matching vector, matrix, and cached-edge overloads and + rejected mismatched edge kinds. Both public edge headers compiled alone. + +## Limits + +Installed cross-compiler and LTO qualification belongs to the release-build +validation that accompanies this change. The complete Sphinx documentation build +and external link check were not run for this migration. diff --git a/bindings/dd/register_matrix_dds.cpp b/bindings/dd/register_matrix_dds.cpp index c0c426185e..98fb60fb53 100644 --- a/bindings/dd/register_matrix_dds.cpp +++ b/bindings/dd/register_matrix_dds.cpp @@ -20,7 +20,6 @@ #include "nanobind/stl/string.h" // NOLINT(misc-include-cleaner) #include "nanobind/stl/vector.h" // NOLINT(misc-include-cleaner) -#include #include #include #include @@ -51,8 +50,8 @@ Matrix getMatrix(const dd::mEdge& m, const size_t numQubits, const auto dim = 1ULL << numQubits; auto dataPtr = std::make_unique(dim * dim); - m.traverseMatrix( - std::complex{1., 0.}, 0ULL, 0ULL, + dd::traverseMatrix( + m, std::complex{1., 0.}, 0ULL, 0ULL, [&dataPtr, dim](const std::size_t i, const std::size_t j, const std::complex& c) { (*dataPtr)[(i * dim) + j] = c; @@ -76,7 +75,9 @@ void registerMatrixDDs(const nb::module_& m) { mat.def("is_zero_terminal", &dd::mEdge::isZeroTerminal, "Check if the DD is a zero terminal node."); - mat.def("is_identity", &dd::mEdge::isIdentity, "up_to_global_phase"_a = true, + mat.def("is_identity", + nb::overload_cast(&dd::isIdentity), + "up_to_global_phase"_a = true, R"pb(Check if the DD represents the identity matrix. Args: @@ -89,8 +90,8 @@ void registerMatrixDDs(const nb::module_& m) { "Get the size of the DD by traversing it once."); mat.def("get_entry", - nb::overload_cast(&dd::mEdge::getValueByIndex, - nb::const_), + nb::overload_cast( + &dd::getValueByIndex), "num_qubits"_a, "row"_a, "col"_a, "Get the entry of the matrix by row and column index."); diff --git a/bindings/dd/register_vector_dds.cpp b/bindings/dd/register_vector_dds.cpp index a325d9149b..d3a315deb3 100644 --- a/bindings/dd/register_vector_dds.cpp +++ b/bindings/dd/register_vector_dds.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Node.hpp" #include "register_dd_export.hpp" @@ -19,7 +20,6 @@ #include "nanobind/stl/string.h" // NOLINT(misc-include-cleaner) #include "nanobind/stl/vector.h" // NOLINT(misc-include-cleaner) -#include #include #include #include @@ -35,7 +35,7 @@ using Vector = nb::ndarray, nb::ndim<1>>; // NOLINTNEXTLINE(misc-use-internal-linkage) Vector getVector(const dd::vEdge& v, const dd::fp threshold) { - auto dataPtr = std::make_unique(v.getVector(threshold)); + auto dataPtr = std::make_unique(dd::getVector(v, threshold)); auto* const data = dataPtr->data(); const auto size = dataPtr->size(); const nb::capsule owner(dataPtr.get(), [](void* ptr) noexcept { @@ -85,7 +85,7 @@ void registerVectorDDs(const nb::module_& m) { } return v.getValueByPath(numQubits, decisions); } - return v.getValueByIndex(index); + return dd::getValueByIndex(v, index); }, "key"_a, "Get the amplitude of a basis state by index."); diff --git a/bindings/mlir/register_mlir.cpp b/bindings/mlir/register_mlir.cpp index 336a59b62a..1c65ce0ac7 100644 --- a/bindings/mlir/register_mlir.cpp +++ b/bindings/mlir/register_mlir.cpp @@ -639,7 +639,7 @@ sampleQCO(const mlir::QCOProgram& program, size_t shots, uint64_t seed) { "dense statevector dimensions exceed addressable memory"); } } - auto dataPtr = std::make_unique(state.getVector()); + auto dataPtr = std::make_unique(dd::getVector(state)); auto* const data = dataPtr->data(); const auto size = dataPtr->size(); const nb::capsule owner(dataPtr.get(), [](void* ptr) noexcept { @@ -662,8 +662,8 @@ sampleQCO(const mlir::QCOProgram& program, size_t shots, uint64_t seed) { } auto dataPtr = std::make_unique(dim * dim); auto* const data = dataPtr->data(); - matrix.traverseMatrix( - std::complex{1., 0.}, 0ULL, 0ULL, + dd::traverseMatrix( + matrix, std::complex{1., 0.}, 0ULL, 0ULL, [data, dim](size_t i, size_t j, const std::complex& value) { data[i * dim + j] = value; }, diff --git a/docs/cpp_api.md b/docs/cpp_api.md index 4d02fd19b0..2ce64c347c 100644 --- a/docs/cpp_api.md +++ b/docs/cpp_api.md @@ -16,6 +16,7 @@ Create a two-qubit GHZ state, which is a Bell state, and print its amplitudes. Save this as `main.cpp`: ```cpp +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" @@ -24,7 +25,7 @@ Save this as `main.cpp`: int main() { dd::Package package(2); const auto state = dd::makeGHZState(2, package); - for (const auto amplitude : state.getVector()) { + for (const auto amplitude : dd::getVector(state)) { std::cout << amplitude << '\n'; } package.decRef(state); diff --git a/include/mqt-core/dd/CachedEdge.hpp b/include/mqt-core/dd/CachedEdge.hpp index 6bea989a1e..95e68ee0ef 100644 --- a/include/mqt-core/dd/CachedEdge.hpp +++ b/include/mqt-core/dd/CachedEdge.hpp @@ -90,44 +90,41 @@ template struct CachedEdge { [[nodiscard]] constexpr bool isTerminal() const { return Node::isTerminal(p); } +}; - /// Get a normalized vector DD from a fresh node and a list of edges. - /// @param p the fresh node - /// @param e the list of edges that form the successor nodes - /// @param mm a reference to the memory manager (for returning unused nodes) - /// @param cn a reference to the complex number manager (for adding new - /// complex numbers) - /// @return the normalized vector DD - static auto normalize(Node* p, const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) -> CachedEdge - requires IsVector; - - /// Get a normalized matrix DD from a fresh node and a list - /// of edges. - /// @param p the fresh node - /// @param e the list of edges that form the successor nodes - /// @param mm a reference to the memory manager (for returning unused nodes) - /// @param cn a reference to the complex number manager (for adding new - /// complex numbers) - /// @return the normalized matrix DD - static auto normalize(Node* p, const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) -> CachedEdge - requires IsMatrix; - - /// Check whether the matrix represented by the DD is the identity. - /// @return whether the matrix is the identity - [[nodiscard]] bool isIdentity(const bool upToGlobalPhase = true) const - requires IsMatrix - { - if (!isTerminal()) { - return false; - } - if (upToGlobalPhase) { - return !w.exactlyZero(); - } - return w.exactlyOne(); +/// Get a normalized vector DD from a fresh node and a list of edges. +/// @param p the fresh node +/// @param e the list of edges that form the successor nodes +/// @param mm a reference to the memory manager (for returning unused nodes) +/// @param cn a reference to the complex number manager (for adding new +/// complex numbers) +/// @return the normalized vector DD +auto normalize(vNode* p, const std::array, RADIX>& e, + MemoryManager& mm, ComplexNumbers& cn) -> CachedEdge; + +/// Get a normalized matrix DD from a fresh node and a list +/// of edges. +/// @param p the fresh node +/// @param e the list of edges that form the successor nodes +/// @param mm a reference to the memory manager (for returning unused nodes) +/// @param cn a reference to the complex number manager (for adding new +/// complex numbers) +/// @return the normalized matrix DD +auto normalize(mNode* p, const std::array, NEDGE>& e, + MemoryManager& mm, ComplexNumbers& cn) -> CachedEdge; + +/// Check whether the matrix represented by the DD is the identity. +/// @return whether the matrix is the identity +[[nodiscard]] inline bool isIdentity(const CachedEdge& edge, + const bool upToGlobalPhase = true) { + if (edge.p != nullptr) { + return false; } -}; + if (upToGlobalPhase) { + return !edge.w.exactlyZero(); + } + return edge.w.exactlyOne(); +} // Deduction guide for constructor: CachedEdge(Node*, const ComplexValue&) template diff --git a/include/mqt-core/dd/Edge.hpp b/include/mqt-core/dd/Edge.hpp index 8e860a8fd4..938ffabc40 100644 --- a/include/mqt-core/dd/Edge.hpp +++ b/include/mqt-core/dd/Edge.hpp @@ -126,149 +126,125 @@ template struct Edge { /// @return the size of the DD [[nodiscard]] std::size_t size(std::unordered_set& visited) const; +}; -public: - /// Get a normalized vector DD from a fresh node and a list of edges - /// @param p the fresh node - /// @param e the list of edges that form the successor nodes - /// @param mm a reference to the memory manager (for returning unused nodes) - /// @param cn a reference to the complex number manager (for adding new - /// complex numbers) - /// @return the normalized vector DD - static auto normalize(Node* p, const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) -> Edge - requires IsVector; - - /// Get a single element of the vector represented by the DD - /// @param i index of the element - /// @throws std::out_of_range if the index is outside the vector - /// @return the complex value of the amplitude - [[nodiscard]] std::complex getValueByIndex(std::size_t i) const - requires IsVector; - - /// Get the vector represented by the DD - /// @param threshold amplitudes with a magnitude below this threshold will be - /// ignored - /// @return the vector - [[nodiscard]] CVec getVector(fp threshold = 0.) const - requires IsVector; - - /// Get the sparse vector represented by the DD - /// @param threshold amplitudes with a magnitude below this threshold will be - /// ignored - /// @return the sparse vector - [[nodiscard]] SparseCVec getSparseVector(fp threshold = 0.) const - requires IsVector; - - /// Print the vector represented by the DD - /// @note This function scales exponentially with the number of qubits. - void printVector() const - requires IsVector; - - /// Add the amplitudes of a vector DD to a vector - /// @param amplitudes the vector to add to - void addToVector(CVec& amplitudes) const - requires IsVector; - -private: - /// Recursively traverse the DD and call a function for each non-zero - /// amplitude. - /// - /// Scales with the number of non-zero amplitudes. - /// @param amp the accumulated amplitude from previous traversals - /// @param i the current index in the vector - /// @param f This function is called for each non-zero amplitude with the - /// index and the amplitude as arguments. - /// @param threshold amplitude with a magnitude below this threshold will be - /// ignored - void traverseVector(const std::complex& amp, std::size_t i, - const AmplitudeFunc& f, fp threshold = 0.) const - requires IsVector; - -public: - /// Get a normalized matrix DD from a fresh node and a list - /// of edges - /// @param p the fresh node - /// @param e the list of edges that form the successor nodes - /// @param mm a reference to the memory manager (for returning unused nodes) - /// @param cn a reference to the complex number manager (for adding new - /// complex numbers) - /// @return the normalized matrix DD - static auto normalize(Node* p, const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) -> Edge - requires IsMatrix; - - /// Check whether the matrix represented by the DD is the identity - /// @return whether the matrix is the identity - [[nodiscard]] bool isIdentity(const bool upToGlobalPhase = true) const - requires IsMatrix - { - if (!isTerminal()) { - return false; - } - if (upToGlobalPhase) { - return !w.exactlyZero(); - } - return w.exactlyOne(); +/// Get a normalized vector DD from a fresh node and a list of edges +/// @param p the fresh node +/// @param e the list of edges that form the successor nodes +/// @param mm a reference to the memory manager (for returning unused nodes) +/// @param cn a reference to the complex number manager (for adding new +/// complex numbers) +/// @return the normalized vector DD +auto normalize(vNode* p, const std::array, RADIX>& e, + MemoryManager& mm, ComplexNumbers& cn) -> Edge; + +/// Get a single element of the vector represented by the DD +/// @param edge the vector DD +/// @param i index of the element +/// @throws std::out_of_range if the index is outside the vector +/// @return the complex value of the amplitude +[[nodiscard]] std::complex getValueByIndex(const Edge& edge, + size_t i); + +/// Get the vector represented by the DD +/// @param edge the vector DD +/// @param threshold amplitudes with a magnitude below this threshold will be +/// ignored +/// @return the vector +[[nodiscard]] CVec getVector(const Edge& edge, fp threshold = 0.); + +/// Get the sparse vector represented by the DD +/// @param edge the vector DD +/// @param threshold amplitudes with a magnitude below this threshold will be +/// ignored +/// @return the sparse vector +[[nodiscard]] SparseCVec getSparseVector(const Edge& edge, + fp threshold = 0.); + +/// Print the vector represented by the DD +/// @note This function scales exponentially with the number of qubits. +void printVector(const Edge& edge); + +/// Add the amplitudes of a vector DD to a vector +/// @param edge the vector DD +/// @param amplitudes the vector to add to +void addToVector(const Edge& edge, CVec& amplitudes); + +/// Get a normalized matrix DD from a fresh node and a list +/// of edges +/// @param p the fresh node +/// @param e the list of edges that form the successor nodes +/// @param mm a reference to the memory manager (for returning unused nodes) +/// @param cn a reference to the complex number manager (for adding new +/// complex numbers) +/// @return the normalized matrix DD +auto normalize(mNode* p, const std::array, NEDGE>& e, + MemoryManager& mm, ComplexNumbers& cn) -> Edge; + +/// Check whether the matrix represented by the DD is the identity +/// @return whether the matrix is the identity +[[nodiscard]] inline bool isIdentity(const Edge& edge, + const bool upToGlobalPhase = true) { + if (edge.p != nullptr) { + return false; } - - /// Get a single element of the matrix represented by the DD - /// @param numQubits number of qubits in the considered DD - /// @param i row index of the element - /// @param j column index of the element - /// @throws std::out_of_range if either index is outside the matrix - /// @return the complex value of the entry - [[nodiscard]] std::complex - getValueByIndex(std::size_t numQubits, std::size_t i, std::size_t j) const - requires IsMatrix; - - /// Get the matrix represented by the DD - /// @param numQubits number of qubits in the considered DD - /// @param threshold entries with a magnitude below this threshold will be - /// ignored - /// @return the matrix - [[nodiscard]] CMat getMatrix(std::size_t numQubits, fp threshold = 0.) const - requires IsMatrix; - - /// Get the sparse matrix represented by the DD - /// @param numQubits number of qubits in the considered DD - /// @param threshold entries with a magnitude below this threshold will be - /// ignored - /// @return the sparse matrix - [[nodiscard]] SparseCMat getSparseMatrix(std::size_t numQubits, - fp threshold = 0.) const - requires IsMatrix; - - /// Print the matrix represented by the DD - /// @param numQubits number of qubits in the considered DD - /// @note This function scales exponentially with the number of qubits. - void printMatrix(std::size_t numQubits) const - requires IsMatrix; - - /// Recursively traverse the DD and call a function for each non-zero - /// matrix entry. - /// - /// One callback instance is used for the entire traversal. - /// @param amp the accumulated amplitude from previous traversals - /// @param i the current row index in the matrix - /// @param j the current column index in the matrix - /// @param f This function is called for each non-zero matrix entry with the - /// row index, the column index and the amplitude as arguments. - /// @param level the current level in the DD (ranges from 1 to n for regular - /// nodes and is 0 for the terminal node) - /// @param threshold entries with a magnitude below this threshold will be - /// ignored - void traverseMatrix(const std::complex& amp, std::size_t i, std::size_t j, - MatrixEntryFunc f, std::size_t level, - fp threshold = 0.) const - requires IsMatrix; - -private: - void traverseMatrixImpl(const std::complex& amp, std::size_t i, - std::size_t j, const MatrixEntryFunc& f, - std::size_t level, fp threshold) const - requires IsMatrix; -}; + if (upToGlobalPhase) { + return !edge.w.exactlyZero(); + } + return edge.w.exactlyOne(); +} + +/// Get a single element of the matrix represented by the DD +/// @param edge the matrix DD +/// @param numQubits number of qubits in the considered DD +/// @param i row index of the element +/// @param j column index of the element +/// @throws std::out_of_range if either index is outside the matrix +/// @return the complex value of the entry +[[nodiscard]] std::complex +getValueByIndex(const Edge& edge, size_t numQubits, size_t i, size_t j); + +/// Get the matrix represented by the DD +/// @param edge the matrix DD +/// @param numQubits number of qubits in the considered DD +/// @param threshold entries with a magnitude below this threshold will be +/// ignored +/// @return the matrix +[[nodiscard]] CMat getMatrix(const Edge& edge, size_t numQubits, + fp threshold = 0.); + +/// Get the sparse matrix represented by the DD +/// @param edge the matrix DD +/// @param numQubits number of qubits in the considered DD +/// @param threshold entries with a magnitude below this threshold will be +/// ignored +/// @return the sparse matrix +[[nodiscard]] SparseCMat getSparseMatrix(const Edge& edge, + size_t numQubits, fp threshold = 0.); + +/// Print the matrix represented by the DD +/// @param edge the matrix DD +/// @param numQubits number of qubits in the considered DD +/// @note This function scales exponentially with the number of qubits. +void printMatrix(const Edge& edge, size_t numQubits); + +/// Recursively traverse the DD and call a function for each non-zero +/// matrix entry. +/// +/// One callback instance is used for the entire traversal. +/// @param edge the matrix DD +/// @param amp the accumulated amplitude from previous traversals +/// @param i the current row index in the matrix +/// @param j the current column index in the matrix +/// @param f This function is called for each non-zero matrix entry with the +/// row index, the column index and the amplitude as arguments. +/// @param level the current level in the DD (ranges from 1 to n for regular +/// nodes and is 0 for the terminal node) +/// @param threshold entries with a magnitude below this threshold will be +/// ignored +void traverseMatrix(const Edge& edge, const std::complex& amp, + size_t i, size_t j, MatrixEntryFunc f, size_t level, + fp threshold = 0.); } // namespace dd template struct std::hash> { diff --git a/include/mqt-core/dd/Package.hpp b/include/mqt-core/dd/Package.hpp index 6abf8f9186..8dbf415ed8 100644 --- a/include/mqt-core/dd/Package.hpp +++ b/include/mqt-core/dd/Package.hpp @@ -540,7 +540,7 @@ class Package { p->flags = 0; } - auto e = EdgeType::normalize(p, edges, memoryManager, cn); + auto e = dd::normalize(p, edges, memoryManager, cn); if constexpr (IsMatrix) { if (!e.isTerminal()) { const auto& es = e.p->e; @@ -763,7 +763,7 @@ class Package { const Qubit var, const std::size_t index) { if constexpr (IsMatrix) { - if (edge.isIdentity() || edge.p->v < var) { + if (dd::isIdentity(edge) || edge.p->v < var) { return index == 0 || index == 3 ? edge : CachedEdge{}; } } @@ -1007,12 +1007,12 @@ class Package { const auto xWeight = static_cast(x.w); const auto yWeight = static_cast(y.w); const auto rWeight = xWeight * yWeight; - if (x.isIdentity()) { + if (dd::isIdentity(x)) { return {y.p, rWeight}; } if constexpr (std::is_same_v) { - if (y.isIdentity()) { + if (dd::isIdentity(y)) { return {x.p, rWeight}; } } @@ -1235,7 +1235,7 @@ class Package { } if constexpr (IsMatrix) { - if (x.isIdentity()) { + if (dd::isIdentity(x)) { return {y.p, rWeight}; } } else { diff --git a/mlir/unittests/Compiler/test_compiler_pipeline.cpp b/mlir/unittests/Compiler/test_compiler_pipeline.cpp index f7fc9fbbc6..e05c3a241d 100644 --- a/mlir/unittests/Compiler/test_compiler_pipeline.cpp +++ b/mlir/unittests/Compiler/test_compiler_pipeline.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/Compiler/Programs.h" #include "mqt/Compiler/QDMIAdapter.h" @@ -2269,7 +2270,7 @@ cx q[0], q[3]; cx q[1], q[3]; const auto expectedDD = qco::buildFunctionality( mlir::mqt::getEntryPoint(program->module()), *package); ASSERT_TRUE(succeeded(expectedDD)); - const auto expected = expectedDD->getMatrix(4); + const auto expected = dd::getMatrix(*expectedDD, 4); package->decRef(*expectedDD); ASSERT_TRUE(program->compileForTarget( @@ -2284,7 +2285,7 @@ cx q[0], q[3]; cx q[1], q[3]; const auto actualDD = qco::buildFunctionality( mlir::mqt::getEntryPoint(program->module()), *package); ASSERT_TRUE(succeeded(actualDD)); - const auto actual = actualDD->getMatrix(4); + const auto actual = dd::getMatrix(*actualDD, 4); package->decRef(*actualDD); const auto physicalIndex = [](size_t basis, const std::vector& sites) { diff --git a/mlir/unittests/Dialect/QC/Translation/test_openqasm3_emission.cpp b/mlir/unittests/Dialect/QC/Translation/test_openqasm3_emission.cpp index 3661b61d5f..7e453ba3e2 100644 --- a/mlir/unittests/Dialect/QC/Translation/test_openqasm3_emission.cpp +++ b/mlir/unittests/Dialect/QC/Translation/test_openqasm3_emission.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/Conversion/QCOToQC/QCOToQC.h" #include "mqt/Conversion/QCToQCO/QCToQCO.h" @@ -1003,8 +1004,8 @@ U(0.4, -0.2, 0.7) q; qco::buildFunctionality(mlir::mqt::getEntryPoint(*roundTrip), package); ASSERT_TRUE(succeeded(before)); ASSERT_TRUE(succeeded(after)); - const auto expected = before->getMatrix(1); - const auto actual = after->getMatrix(1); + const auto expected = dd::getMatrix(*before, 1); + const auto actual = dd::getMatrix(*after, 1); for (size_t row = 0; row < 2; ++row) { for (size_t column = 0; column < 2; ++column) { EXPECT_NEAR(std::abs(actual[row][column] - expected[row][column]), 0., @@ -1101,7 +1102,7 @@ wrapper(0.25) q; auto functionality = qco::buildFunctionality(mlir::mqt::getEntryPoint(moduleOp), package); ASSERT_TRUE(succeeded(functionality)); - const auto matrix = functionality->getMatrix(1); + const auto matrix = dd::getMatrix(*functionality, 1); /// The six iterations sum to RX(6 * 0.25 + 3 * (1/2 + 1/3)) = RX(4). for (size_t row = 0; row < 2; ++row) { for (size_t column = 0; column < 2; ++column) { @@ -1830,7 +1831,7 @@ TEST(OpenQASM3EmissionTest, PreservesControlledGatesAndAngleTables) { auto functionality = qco::buildFunctionality( restored->lookupSymbol("main"), package); ASSERT_TRUE(succeeded(functionality)); - const auto matrix = functionality->getMatrix(2); + const auto matrix = dd::getMatrix(*functionality, 2); for (size_t row = 0; row < 4; ++row) { for (size_t column = 0; column < 4; ++column) { const auto expected = row != column ? 0.0 : row == 3 ? -1.0 : 1.0; @@ -2736,11 +2737,11 @@ TEST(OpenQASM3EmissionTest, auto lhs = qco::buildFunctionality( original->lookupSymbol("main"), package); ASSERT_TRUE(succeeded(lhs)); - const auto expected = lhs->getMatrix(width); + const auto expected = dd::getMatrix(*lhs, width); auto rhs = qco::buildFunctionality( restored->lookupSymbol("main"), package); ASSERT_TRUE(succeeded(rhs)); - const auto actual = rhs->getMatrix(width); + const auto actual = dd::getMatrix(*rhs, width); bool nonIdentity = false; for (size_t row = 0; row < expected.size(); ++row) { for (size_t column = 0; column < expected.size(); ++column) { diff --git a/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp index 34303455b1..fa8c1c10bd 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/Compiler/Target.h" #include "mqt/Dialect/MQT/Utils/DenseUnitary.h" @@ -558,7 +559,7 @@ computeTwoQubitUnitaryFromFunc(func::FuncOp funcOp) { } // `getMatrix` is DD/LSB-first; QCO is MSB-first — index `1 ↔ 2` swaps the // middle basis states (`|01⟩` ↔ `|10⟩`). - const auto& m = u->getMatrix(2); + const auto& m = dd::getMatrix(*u, 2); const Matrix4x4 matrix = Matrix4x4::fromElements( m[0][0], m[0][2], m[0][1], m[0][3], m[2][0], m[2][2], m[2][1], m[2][3], m[1][0], m[1][2], m[1][1], m[1][3], m[3][0], m[3][2], m[3][1], m[3][3]); diff --git a/mlir/unittests/Dialect/QCO/Utils/test_dd_functionality.cpp b/mlir/unittests/Dialect/QCO/Utils/test_dd_functionality.cpp index 33c6a10d67..fd7eb1ac79 100644 --- a/mlir/unittests/Dialect/QCO/Utils/test_dd_functionality.cpp +++ b/mlir/unittests/Dialect/QCO/Utils/test_dd_functionality.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Node.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" @@ -214,7 +215,7 @@ class QCODDFunctionalityTest : public testing::Test { const auto fromQcoSim = simulate(func, dd::makeZeroState(numQubits, *dd), *dd, rng); ASSERT_TRUE(succeeded(fromQcoSim)); - EXPECT_EQ(fromQcoSim->getVector(), referenceSim.getVector()); + EXPECT_EQ(dd::getVector(*fromQcoSim), dd::getVector(referenceSim)); dd->decRef(*fromQcoSim); dd->decRef(referenceSim); } @@ -234,7 +235,7 @@ class QCODDFunctionalityTest : public testing::Test { } const auto out = simulate(func, dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(out)); - EXPECT_EQ(out->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(expected)); dd->decRef(*out); dd->decRef(expected); } @@ -325,9 +326,9 @@ TEST(DDAdapterTest, PreservesComplexMatricesAcrossIdleWires) { std::array{0, 1, 2, 3}, std::array{4, 1, 5, 2}, }) { - const auto matrix = - makeGateDD(package, toDynamicMatrix(local), numQubits, targets) - .getMatrix(numQubits); + const auto matrix = dd::getMatrix( + makeGateDD(package, toDynamicMatrix(local), numQubits, targets), + numQubits); size_t targetMask = 0; for (const auto wire : targets) { targetMask |= size_t{1} << wire; @@ -652,8 +653,8 @@ TEST_F(QCODDFunctionalityTest, Gphase) { ASSERT_TRUE(succeeded(u0)); ASSERT_TRUE(succeeded(u1)); const auto phase = std::polar(1.0, 0.25); - const auto m0 = u0->getMatrix(1); - const auto m1 = u1->getMatrix(1); + const auto m0 = dd::getMatrix(*u0, 1); + const auto m1 = dd::getMatrix(*u1, 1); for (size_t r = 0; r < 2; ++r) { for (size_t c = 0; c < 2; ++c) { EXPECT_TRUE(std::abs(m1[r][c] - (m0[r][c] * phase)) < 1e-10); @@ -797,7 +798,7 @@ TEST_F(QCODDFunctionalityTest, SimulationConsumesInputReference) { const auto widerOutput = simulate( mainFunc(*valid), dd::makeZeroState(2, *twoQubitDd), *twoQubitDd, rng); ASSERT_TRUE(succeeded(widerOutput)); - EXPECT_EQ(widerOutput->getVector().size(), 4U); + EXPECT_EQ(dd::getVector(*widerOutput).size(), 4U); twoQubitDd->decRef(*widerOutput); EXPECT_TRUE(twoQubitDd->getRootSet().empty()); @@ -832,7 +833,7 @@ TEST_F(QCODDFunctionalityTest, auto expected = dd->applyOperation(referenceGateDD(*dd, {1}), dd::makeZeroState(3, *dd)); expected = dd->applyOperation(referenceGateDD(*dd, {2}), expected); - EXPECT_EQ(output->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*output), dd::getVector(expected)); dd->decRef(*output); dd->decRef(expected); } @@ -853,13 +854,13 @@ TEST_F(QCODDFunctionalityTest, SimulateMeasureCollapsesLikePackage) { auto ref = dd::makeZeroState(1, *dd); ref = dd->applyOperation(referenceGateDD(*dd, {0}), ref); static_cast(dd->measureOneCollapsing(ref, 0, refRng)); - const auto expected = ref.getVector(); + const auto expected = dd::getVector(ref); std::mt19937_64 rng(seed); const auto out = simulate(mainFunc(*mod), dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(out)); - EXPECT_EQ(out->getVector(), expected); + EXPECT_EQ(dd::getVector(*out), expected); dd->decRef(*out); dd->decRef(ref); } @@ -879,7 +880,7 @@ TEST_F(QCODDFunctionalityTest, SimulateResetForcesZero) { const auto out = simulate(mainFunc(*mod), dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(out)); - EXPECT_EQ(out->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(expected)); dd->decRef(*out); dd->decRef(expected); } @@ -958,7 +959,7 @@ TEST_F(QCODDFunctionalityTest, SimulateMeasureFeedsIf) { const auto out = simulate(mainFunc(*mod), dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(out)); - EXPECT_EQ(out->getVector(), one.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(one)); dd->decRef(*out); dd->decRef(one); } @@ -997,12 +998,12 @@ TEST_F(QCODDFunctionalityTest, SimulateCBitConditionAndMeasurementUpdate) { const auto zeroOut = simulate(mainFunc(*zeroCondition), dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(zeroOut)); - EXPECT_EQ(zeroOut->getVector(), zero.getVector()); + EXPECT_EQ(dd::getVector(*zeroOut), dd::getVector(zero)); const auto measurementOut = simulate(mainFunc(*measurementCondition), dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(measurementOut)); - EXPECT_EQ(measurementOut->getVector(), one.getVector()); + EXPECT_EQ(dd::getVector(*measurementOut), dd::getVector(one)); dd->decRef(*zeroOut); dd->decRef(*measurementOut); @@ -1130,7 +1131,7 @@ TEST_F(QCODDFunctionalityTest, SimulateMeasureFeedsIndexSwitch) { const auto out = simulate(mainFunc(*mod), dd::makeZeroState(1, *dd), *dd, rng); ASSERT_TRUE(succeeded(out)); - EXPECT_EQ(out->getVector(), zero.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(zero)); dd->decRef(*out); dd->decRef(zero); } @@ -1206,7 +1207,7 @@ TEST_F(QCODDFunctionalityTest, SimulateAndiOriShliClassical) { const auto out = simulate(mainFunc(*mod), dd::makeZeroState(3, *dd), *dd, rng); ASSERT_TRUE(succeeded(out)); - EXPECT_EQ(out->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(expected)); dd->decRef(*out); dd->decRef(expected); } @@ -1235,7 +1236,7 @@ TEST_F(QCODDFunctionalityTest, AcceptsLargestValidShift) { ASSERT_TRUE(succeeded(out)); auto expected = dd->applyOperation(referenceGateDD(*dd, {0}), dd::makeZeroState(1, *dd)); - EXPECT_EQ(out->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(expected)); dd->decRef(*out); dd->decRef(expected); } @@ -1563,7 +1564,7 @@ TEST_F(QCODDFunctionalityTest, BindsClassicalIfResults) { ASSERT_TRUE(succeeded(out)); auto expected = dd->applyOperation(referenceGateDD(*dd, {0}), dd::makeZeroState(1, *dd)); - EXPECT_EQ(out->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(expected)); dd->decRef(*out); dd->decRef(expected); } @@ -1604,7 +1605,7 @@ TEST_F(QCODDFunctionalityTest, BindsClassicalIndexResults) { ASSERT_TRUE(succeeded(out)); auto expected = dd->applyOperation(referenceGateDD(*dd, {0}), dd::makeZeroState(1, *dd)); - EXPECT_EQ(out->getVector(), expected.getVector()); + EXPECT_EQ(dd::getVector(*out), dd::getVector(expected)); dd->decRef(*out); dd->decRef(expected); } @@ -1836,7 +1837,7 @@ TEST_F(QCODDFunctionalityTest, ExecuteParameterizedGateCallsWithGlobalPhase) { dd::Package package(1); auto result = buildFunctionality(function, package, bindings); ASSERT_TRUE(succeeded(result)); - const auto matrix = result->getMatrix(1); + const auto matrix = dd::getMatrix(*result, 1); for (size_t row = 0; row < 2; ++row) { for (size_t column = 0; column < 2; ++column) { EXPECT_NEAR(std::abs(matrix[row][column] - (row == column ? 0. : -1.)), @@ -2069,7 +2070,7 @@ TEST_F(QCODDFunctionalityTest, ParameterizedUnitaryCallsUseClassicalBindings) { dd::Package package(1); auto matrix = buildFunctionality(func, package, bindings); ASSERT_TRUE(succeeded(matrix)); - const auto dense = matrix->getMatrix(1); + const auto dense = dd::getMatrix(*matrix, 1); // Two rotations and their global phases give iX, checking the phase too. EXPECT_NEAR(std::abs(dense[0][0]), 0., 1e-12); EXPECT_NEAR(std::abs(dense[1][1]), 0., 1e-12); @@ -2078,7 +2079,7 @@ TEST_F(QCODDFunctionalityTest, ParameterizedUnitaryCallsUseClassicalBindings) { package.decRef(*matrix); auto state = simulateStatevector(func, package, bindings); ASSERT_TRUE(succeeded(state)); - EXPECT_NEAR(std::abs(state->getVector()[1] - std::complex(0., 1.)), + EXPECT_NEAR(std::abs(dd::getVector(*state)[1] - std::complex(0., 1.)), 0., 1e-12); package.decRef(*state); auto counts = sample(func, 8, 17, bindings); @@ -2849,12 +2850,12 @@ TEST_F(QCODDFunctionalityTest, FixedGatePowersPreserveFullMatrix) { auto expected = buildFunctionality(before, package, {{before.getArgument(0), value}}); ASSERT_TRUE(succeeded(expected)); - const auto left = expected->getMatrix(1); + const auto left = dd::getMatrix(*expected, 1); for (auto function : {after, constantFunction}) { auto actual = buildFunctionality(function, package, {{function.getArgument(0), value}}); ASSERT_TRUE(succeeded(actual)); - const auto right = actual->getMatrix(1); + const auto right = dd::getMatrix(*actual, 1); for (size_t row = 0; row < 2; ++row) { for (size_t column = 0; column < 2; ++column) { EXPECT_NEAR(std::abs(left[row][column] - right[row][column]), 0., @@ -2901,7 +2902,7 @@ TEST_F(QCODDFunctionalityTest, SymbolicParametersUseBindings) { auto expected = buildFunctionality(mainFunc(*concrete), *dd); ASSERT_TRUE(succeeded(actual)); ASSERT_TRUE(succeeded(expected)); - EXPECT_EQ(actual->getMatrix(1), expected->getMatrix(1)); + EXPECT_EQ(dd::getMatrix(*actual, 1), dd::getMatrix(*expected, 1)); dd->decRef(*actual); dd->decRef(*expected); @@ -2911,7 +2912,7 @@ TEST_F(QCODDFunctionalityTest, SymbolicParametersUseBindings) { const auto state = simulateStatevector(func, *dd, bindings); ASSERT_TRUE(succeeded(state)); - const auto vector = state->getVector(); + const auto vector = dd::getVector(*state); ASSERT_EQ(vector.size(), 2U); EXPECT_NEAR(std::norm(vector[0]), 0.0, 1e-12); EXPECT_NEAR(std::norm(vector[1]), 1.0, 1e-12); @@ -3090,13 +3091,13 @@ TEST_F(QCODDFunctionalityTest, AllocationPreservesComplexInputAmplitudes) { dd::makeStateFromVector(input, package), package, rng); ASSERT_TRUE(succeeded(state)); const dd::CVec expected{input[0], input[1], 0., 0.}; - const auto actual = state->getVector(); + const auto actual = dd::getVector(*state); ASSERT_EQ(actual.size(), expected.size()); for (size_t i = 0; i < actual.size(); ++i) { EXPECT_NEAR(std::abs(actual[i] - expected[i]), 0., 1e-12); } package.garbageCollect(true); - EXPECT_EQ(state->getVector(), actual); + EXPECT_EQ(dd::getVector(*state), actual); package.decRef(*state); package.garbageCollect(true); EXPECT_EQ(package.vUniqueTable.getNumEntries(), 0); @@ -3639,7 +3640,7 @@ TEST_F(QCODDFunctionalityTest, StatevectorSupportsTerminalMeasurements) { const auto state = simulateStatevector(mainFunc(*mod), *dd); ASSERT_TRUE(succeeded(state)); EXPECT_EQ(dd->qubits(), 1U); - const auto vector = state->getVector(); + const auto vector = dd::getVector(*state); ASSERT_EQ(vector.size(), 2U); EXPECT_NEAR(std::norm(vector[0]), 0.5, 1e-12); EXPECT_NEAR(std::norm(vector[1]), 0.5, 1e-12); @@ -3696,7 +3697,7 @@ TEST_F(QCODDFunctionalityTest, LifetimeMarkersPreserveEntangledState) { auto dd = std::make_unique(0); const auto state = simulateStatevector(mainFunc(*mod), *dd); ASSERT_TRUE(succeeded(state)); - const auto vector = state->getVector(); + const auto vector = dd::getVector(*state); ASSERT_EQ(vector.size(), 8U); EXPECT_NEAR(std::norm(vector[4]), 0.5, 1e-12); EXPECT_NEAR(std::norm(vector[7]), 0.5, 1e-12); @@ -3731,7 +3732,7 @@ TEST_F(QCODDFunctionalityTest, StatevectorSkipsUnreachedAllocations) { const auto state = simulateStatevector(mainFunc(*mod), *dd); ASSERT_TRUE(succeeded(state)); EXPECT_EQ(dd->qubits(), 0U); - const auto vector = state->getVector(); + const auto vector = dd::getVector(*state); ASSERT_EQ(vector.size(), 1U); EXPECT_NEAR(std::norm(vector[0]), 1.0, 1e-12); dd->decRef(*state); diff --git a/mlir/unittests/Dialect/QIR/Execution/JIT/test_jit_session.cpp b/mlir/unittests/Dialect/QIR/Execution/JIT/test_jit_session.cpp index 761f74a3e4..e6a7620f8a 100644 --- a/mlir/unittests/Dialect/QIR/Execution/JIT/test_jit_session.cpp +++ b/mlir/unittests/Dialect/QIR/Execution/JIT/test_jit_session.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "mqt/Compiler/Programs.h" #include "mqt/Dialect/MQT/IR/MQTDialect.h" #include "mqt/Dialect/QCO/Utils/DDFunctionality.h" @@ -198,7 +199,7 @@ TEST_F(JitSessionTest, StateExtractionSupportsAdaptiveControlAndLifetimes) { EXPECT_TRUE(sink.str().empty()); auto state = session.runtime().takeState(); EXPECT_EQ(state.numQubits, 4); - const auto values = state.edge.getVector(); + const auto values = dd::getVector(state.edge); ASSERT_EQ(values.size(), 16); for (size_t i = 0; i < values.size(); ++i) { const auto expected = i == 4 || i == 7 ? std::polar(dd::SQRT2_2, 0.3) @@ -580,7 +581,7 @@ attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "required_num_ EXPECT_FALSE(available); } EXPECT_EQ(state.numQubits, 2); - const auto vector = state.edge.getVector(); + const auto vector = dd::getVector(state.edge); ASSERT_EQ(vector.size(), 4); EXPECT_NEAR(std::abs(vector[2] - std::polar(1., 0.3)), 0., 1e-12); EXPECT_NEAR(std::abs(vector[0]) + std::abs(vector[1]) + std::abs(vector[3]), @@ -662,7 +663,7 @@ attributes #0 = { "entry_point" "qir_profiles"="base_profile" "required_num_qubi auto state = session.runtime().takeState(); EXPECT_EQ(state.numQubits, 3); EXPECT_EQ(state.dd->qubits(), 3); - const auto values = state.edge.getVector(); + const auto values = dd::getVector(state.edge); ASSERT_EQ(values.size(), 8); EXPECT_EQ(values[1], 1.); state.dd->decRef(state.edge); diff --git a/mlir/unittests/Dialect/QIR/Execution/Runtime/test_qir_runtime.cpp b/mlir/unittests/Dialect/QIR/Execution/Runtime/test_qir_runtime.cpp index 95ef7afee0..6abf0b8a29 100644 --- a/mlir/unittests/Dialect/QIR/Execution/Runtime/test_qir_runtime.cpp +++ b/mlir/unittests/Dialect/QIR/Execution/Runtime/test_qir_runtime.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "mqt/Dialect/QCO/IR/QCOOps.h" #include "mqt/Dialect/QCO/Utils/DDAdapter.h" #include "mqt/Dialect/QIR/Execution/Runtime/QIR.h" @@ -266,7 +267,7 @@ TEST_F(QIRRuntimeTest, GlobalPhase) { __quantum__qis__gphase__body(dd::PI_2); const auto state = Runtime::getInstance().takeState(); - const auto vector = state.edge.getVector(); + const auto vector = dd::getVector(state.edge); ASSERT_EQ(vector.size(), 2); EXPECT_NEAR(vector[0].real(), 0., 1e-12); EXPECT_NEAR(vector[0].imag(), 1., 1e-12); @@ -911,7 +912,7 @@ TEST_F(QIRRuntimeTest, PreservesPhaseBeforeFirstQubitAndFollowingGates) { __quantum__qis__x__body(nullptr); auto state = Runtime::getInstance().takeState(); state.dd->garbageCollect(true); - const auto values = state.edge.getVector(); + const auto values = dd::getVector(state.edge); ASSERT_EQ(values.size(), 2); EXPECT_NEAR(std::abs(values[0] - std::polar(1., 0.7)), 0., 1e-12); EXPECT_EQ(values[1], 0.); @@ -928,7 +929,7 @@ TEST_F(QIRRuntimeTest, ExtractsLogicalOrderAfterSwapCycle) { __quantum__qis__swap__body(q0, q1); __quantum__qis__swap__body(q1, q2); auto state = Runtime::getInstance().takeState(); - const auto values = state.edge.getVector(); + const auto values = dd::getVector(state.edge); ASSERT_EQ(values.size(), 8); for (size_t i = 0; i < values.size(); ++i) { EXPECT_EQ(values[i], i == 4 ? 1. : 0.); diff --git a/mlir/unittests/ExactUnitaryTest.h b/mlir/unittests/ExactUnitaryTest.h index c5230fb7b5..26462785cf 100644 --- a/mlir/unittests/ExactUnitaryTest.h +++ b/mlir/unittests/ExactUnitaryTest.h @@ -10,6 +10,7 @@ #pragma once +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/Dialect/QCO/Utils/DDFunctionality.h" @@ -45,8 +46,8 @@ inline void expectFullUnitaryEqual(mlir::ModuleOp expectedModule, ASSERT_TRUE(mlir::succeeded(expected)); ASSERT_TRUE(mlir::succeeded(actual)); - const auto expectedMatrix = expected->getMatrix(numQubits); - const auto actualMatrix = actual->getMatrix(numQubits); + const auto expectedMatrix = dd::getMatrix(*expected, numQubits); + const auto actualMatrix = dd::getMatrix(*actual, numQubits); ASSERT_EQ(expectedMatrix.size(), actualMatrix.size()); for (std::size_t row = 0; row < expectedMatrix.size(); ++row) { ASSERT_EQ(expectedMatrix[row].size(), actualMatrix[row].size()); diff --git a/mlir/unittests/bench/test_benchmark_generate_modular_multiplier.cpp b/mlir/unittests/bench/test_benchmark_generate_modular_multiplier.cpp index 7c86cddeef..3df6660a7c 100644 --- a/mlir/unittests/bench/test_benchmark_generate_modular_multiplier.cpp +++ b/mlir/unittests/bench/test_benchmark_generate_modular_multiplier.cpp @@ -10,6 +10,7 @@ #include "bench/ModularMultiplier.hpp" #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/bench/Generate.h" @@ -51,7 +52,7 @@ static void expectCoherentModularMultiplier(const size_t bits, auto state = qco::simulateStatevector( mlir::mqt::getEntryPoint(program->module()), package); ASSERT_TRUE(succeeded(state)); - const auto actual = state->getVector(); + const auto actual = dd::getVector(*state); package.decRef(*state); dd::CVec expected(size_t{1} << ((2U * bits) + 3U)); diff --git a/mlir/unittests/bench/test_benchmark_generate_qft_adder.cpp b/mlir/unittests/bench/test_benchmark_generate_qft_adder.cpp index 2867c62bb3..7fcb96d7f1 100644 --- a/mlir/unittests/bench/test_benchmark_generate_qft_adder.cpp +++ b/mlir/unittests/bench/test_benchmark_generate_qft_adder.cpp @@ -12,6 +12,7 @@ #include "bench/JSON.hpp" #include "bench/QFTAdder.hpp" #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/Dialect/QC/IR/QCOps.h" #include "mqt/bench/Generate.h" @@ -193,7 +194,7 @@ TEST(GenerateProgramTest, PreservesQFTAdderRelativePhases) { auto state = qco::simulateStatevector( mlir::mqt::getEntryPoint(program->module()), package); ASSERT_TRUE(succeeded(state)); - const auto actual = state->getVector(); + const auto actual = dd::getVector(*state); package.decRef(*state); dd::CVec expected(size_t{1} << (width + sumWidth)); for (size_t addend = 0; addend < (size_t{1} << width); ++addend) { diff --git a/mlir/unittests/bench/test_benchmark_generate_shor.cpp b/mlir/unittests/bench/test_benchmark_generate_shor.cpp index ea7bc12471..cc13e46148 100644 --- a/mlir/unittests/bench/test_benchmark_generate_shor.cpp +++ b/mlir/unittests/bench/test_benchmark_generate_shor.cpp @@ -10,6 +10,7 @@ #include "bench/Shor.hpp" #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" #include "mqt/Compiler/Programs.h" @@ -167,7 +168,7 @@ TEST(GenerateProgramTest, VerifiesSmallInPlaceMultiplierBasisStates) { input[(value << 1U) | control] = 1.; auto state = package.applyOperation( *functionality, dd::makeStateFromVector(input, package)); - auto output = state.getVector(); + auto output = dd::getVector(state); package.decRef(state); const auto product = control != 0 ? multiplier * value % number : value; @@ -206,7 +207,7 @@ TEST(GenerateProgramTest, PreservesMultiplierCoherenceAndUncomputesWorkspace) { ASSERT_TRUE(succeeded(functionality)); auto state = package.applyOperation( *functionality, dd::makeStateFromVector(input, package)); - auto output = state.getVector(); + auto output = dd::getVector(state); package.decRef(state); package.decRef(*functionality); ASSERT_EQ(output.size(), expected.size()); diff --git a/src/dd/CachedEdge.cpp b/src/dd/CachedEdge.cpp index c6391de950..4d0c5cdb48 100644 --- a/src/dd/CachedEdge.cpp +++ b/src/dd/CachedEdge.cpp @@ -28,17 +28,12 @@ namespace dd { -///----------------------------------------------------------------------------- -/// \n Methods for vector DDs \n -///----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// \n Methods for vector DDs \n +//----------------------------------------------------------------------------- -template -auto CachedEdge::normalize(Node* p, - const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) - -> CachedEdge - requires IsVector -{ +auto normalize(vNode* p, const std::array, RADIX>& e, + MemoryManager& mm, ComplexNumbers& cn) -> CachedEdge { assert(p != nullptr && "Node pointer passed to normalize is null."); const auto zero = std::array{e[0].w.approximatelyZero(), e[1].w.approximatelyZero()}; @@ -46,32 +41,32 @@ auto CachedEdge::normalize(Node* p, if (zero[0]) { if (zero[1]) { mm.returnEntry(*p); - return CachedEdge::zero(); + return vCachedEdge::zero(); } - p->e = {vEdge::zero(), {e[1].p, Complex::one()}}; + p->e = {vEdge::zero(), {.p = e[1].p, .w = Complex::one()}}; return {p, e[1].w}; } if (zero[1]) { - p->e = {vEdge{e[0].p, Complex::one()}, vEdge::zero()}; + p->e = {vEdge{.p = e[0].p, .w = Complex::one()}, vEdge::zero()}; return {p, e[0].w}; } - /// Project nearly equal or opposite coefficients before normalization can - /// amplify their difference. For unit-norm children, the local Euclidean - /// error is at most eps before roundoff. + // Project nearly equal or opposite coefficients before normalization can + // amplify their difference. For unit-norm children, the local Euclidean + // error is at most eps before roundoff. for (const fp sign : {1., -1.}) { const auto other = e[1].w * sign; if (e[0].w.approximatelyEquals(other)) { - p->e[0] = {e[0].p, cn.lookup(SQRT2_2)}; - p->e[1] = {e[1].p, cn.lookup(sign * SQRT2_2)}; + p->e[0] = {.p = e[0].p, .w = cn.lookup(SQRT2_2)}; + p->e[1] = {.p = e[1].p, .w = cn.lookup(sign * SQRT2_2)}; return {p, (e[0].w + other) * SQRT2_2}; } } const auto mag2 = std::array{e[0].w.mag2(), e[1].w.mag2()}; - /// Keep the dominant phase independent of the incoming scale. + // Keep the dominant phase independent of the incoming scale. const auto argMax = mag2[1] - mag2[0] > RealNumber::eps * std::max(mag2[0], mag2[1]) ? 1U : 0U; @@ -83,8 +78,8 @@ auto CachedEdge::normalize(Node* p, const auto norm = std::sqrt(maxMag2 + minMag2); const auto maxMag = std::sqrt(maxMag2); const auto maxWeight = maxMag / norm; - p->e[argMax] = {e[argMax].p, cn.lookup(maxWeight)}; - /// Preserve the dominant coefficient after interning its normalized weight. + p->e[argMax] = {.p = e[argMax].p, .w = cn.lookup(maxWeight)}; + // Preserve the dominant coefficient after interning its normalized weight. const auto topWeight = e[argMax].w / RealNumber::val(p->e[argMax].w.r); const auto minWeight = e[argMin].w / topWeight; assert(!p->e[argMax].w.exactlyZero() && @@ -96,23 +91,18 @@ auto CachedEdge::normalize(Node* p, "Edge weight should be one when minWeight is zero."); p->e[argMin] = vEdge::zero(); } else { - p->e[argMin] = {e[argMin].p, minW}; + p->e[argMin] = {.p = e[argMin].p, .w = minW}; } return {p, topWeight}; } -///----------------------------------------------------------------------------- -/// \n Methods for matrix DDs \n -///----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// \n Methods for matrix DDs \n +//----------------------------------------------------------------------------- -template -auto CachedEdge::normalize(Node* p, - const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) - -> CachedEdge - requires IsMatrix -{ +auto normalize(mNode* p, const std::array, NEDGE>& e, + MemoryManager& mm, ComplexNumbers& cn) -> CachedEdge { assert(p != nullptr && "Node pointer passed to normalize is null."); const auto zero = std::array{ e[0].w.approximatelyZero(), @@ -121,13 +111,13 @@ auto CachedEdge::normalize(Node* p, e[3].w.approximatelyZero(), }; - if (std::all_of(zero.begin(), zero.end(), [](auto b) { return b; })) { + if (std::ranges::all_of(zero, [](auto b) { return b; })) { mm.returnEntry(*p); - return CachedEdge::zero(); + return mCachedEdge::zero(); } - /// The incoming scale does not affect normalized coefficients. Remove it - /// before squared magnitudes and complex division can overflow or underflow. + // The incoming scale does not affect normalized coefficients. Remove it + // before squared magnitudes and complex division can overflow or underflow. const auto maxComponent = std::max({ std::abs(e[0].w.r), std::abs(e[0].w.i), @@ -146,7 +136,7 @@ auto CachedEdge::normalize(Node* p, } } - std::optional argMax = std::nullopt; + std::optional argMax = std::nullopt; fp maxMag2 = 0.; ComplexValue maxVal = 1.; // determine max amplitude @@ -172,31 +162,24 @@ auto CachedEdge::normalize(Node* p, const auto argMaxValue = *argMax; for (auto i = 0U; i < NEDGE; ++i) { - /// Treat weights within tolerance as zero before normalization amplifies - /// them. + // Treat weights within tolerance as zero before normalization amplifies + // them. if (zero[i]) { - p->e[i] = Edge::zero(); + p->e[i] = Edge::zero(); continue; } if (i == argMaxValue) { - p->e[i] = {e[i].p, Complex::one()}; + p->e[i] = {.p = e[i].p, .w = Complex::one()}; continue; } - p->e[i] = {e[i].p, cn.lookup(weights[i] / weights[argMaxValue])}; + p->e[i] = {.p = e[i].p, .w = cn.lookup(weights[i] / weights[argMaxValue])}; if (p->e[i].w.exactlyZero()) { - p->e[i].p = Node::getTerminal(); + p->e[i].p = mNode::getTerminal(); } } - return CachedEdge{p, maxVal}; + return mCachedEdge{p, maxVal}; } -///----------------------------------------------------------------------------- -/// \n Explicit instantiations \n -///----------------------------------------------------------------------------- - -template struct CachedEdge; -template struct CachedEdge; - } // namespace dd template diff --git a/src/dd/Edge.cpp b/src/dd/Edge.cpp index 444c0af186..0e99863544 100644 --- a/src/dd/Edge.cpp +++ b/src/dd/Edge.cpp @@ -35,10 +35,70 @@ #include namespace dd { +namespace { -///----------------------------------------------------------------------------- -/// \n General purpose methods \n -///----------------------------------------------------------------------------- +void traverseVector(const vEdge& edge, const std::complex& amp, + const size_t i, const AmplitudeFunc& f, + const fp threshold) { + const auto c = amp * static_cast>(edge.w); + + if (threshold > 0. && std::abs(c) < threshold) { + return; + } + + if (edge.isTerminal()) { + f(i, c); + return; + } + + // recursive case + if (const auto& e = edge.p->e[0]; !e.w.exactlyZero()) { + traverseVector(e, c, i, f, threshold); + } + if (const auto& e = edge.p->e[1]; !e.w.exactlyZero()) { + traverseVector(e, c, i | (1ULL << edge.p->v), f, threshold); + } +} + +void traverseMatrixImpl(const mEdge& edge, const std::complex& amp, + const size_t i, const size_t j, + const MatrixEntryFunc& f, const size_t level, + const fp threshold) { + const auto c = amp * static_cast>(edge.w); + + if (threshold > 0. && std::abs(c) < threshold) { + return; + } + + if (level == 0) { + assert(edge.isTerminal()); + f(i, j, c); + return; + } + + const auto nextLevel = static_cast(level - 1U); + const size_t x = i | (1ULL << nextLevel); + const size_t y = j | (1ULL << nextLevel); + if (edge.isTerminal() || edge.p->v < nextLevel) { + traverseMatrixImpl(edge, amp, i, j, f, nextLevel, threshold); + traverseMatrixImpl(edge, amp, x, y, f, nextLevel, threshold); + return; + } + + const auto coords = {std::pair{i, j}, {i, y}, {x, j}, {x, y}}; + size_t k = 0U; + for (const auto& [a, b] : coords) { + if (auto const& e = edge.p->e[k++]; !e.w.exactlyZero()) { + traverseMatrixImpl(e, c, a, b, f, nextLevel, threshold); + } + } +} + +} // namespace + +//----------------------------------------------------------------------------- +// \n General purpose methods \n +//----------------------------------------------------------------------------- template auto Edge::getValueByPath(const std::size_t numQubits, @@ -133,15 +193,12 @@ template void Edge::unmark() const noexcept { } } -///----------------------------------------------------------------------------- -/// \n Methods for vector DDs \n -///----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// \n Methods for vector DDs \n +//----------------------------------------------------------------------------- -template -auto Edge::normalize(Node* p, const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) -> Edge - requires IsVector -{ +auto normalize(vNode* p, const std::array, RADIX>& e, + MemoryManager& mm, ComplexNumbers& cn) -> Edge { assert(p != nullptr && "Node pointer passed to normalize is null."); const auto zero = std::array{e[0].w.exactlyZero(), e[1].w.exactlyZero()}; @@ -151,14 +208,14 @@ auto Edge::normalize(Node* p, const std::array& e, return vEdge::zero(); } p->e = e; - vEdge r{p, e[1].w}; + vEdge r{.p = p, .w = e[1].w}; p->e[1].w = Complex::one(); return r; } p->e = e; if (zero[1]) { - vEdge r{p, e[0].w}; + vEdge r{.p = p, .w = e[0].w}; p->e[0].w = Complex::one(); return r; } @@ -170,7 +227,7 @@ auto Edge::normalize(Node* p, const std::array& e, const auto mag2 = std::array{weights[0].mag2(), weights[1].mag2()}; - /// Keep the dominant phase independent of the incoming scale. + // Keep the dominant phase independent of the incoming scale. const auto argMax = mag2[1] - mag2[0] > RealNumber::eps * std::max(mag2[0], mag2[1]) ? 1U : 0U; @@ -183,15 +240,15 @@ auto Edge::normalize(Node* p, const std::array& e, const auto maxMag = std::sqrt(maxMag2); const auto maxWeight = maxMag / norm; p->e[argMax].w = cn.lookup(maxWeight); - /// Preserve the dominant coefficient after interning its normalized weight. + // Preserve the dominant coefficient after interning its normalized weight. const auto topWeight = weights[argMax] / RealNumber::val(p->e[argMax].w.r); assert(!p->e[argMax].w.exactlyZero() && "Max edge weight should not be zero."); - vEdge r = {p, cn.lookup(topWeight)}; + vEdge r = {.p = p, .w = cn.lookup(topWeight)}; assert(!r.w.exactlyZero() && "Top edge weight should not be zero."); - /// Lookup can round the top weight; normalize against the stored value. + // Lookup can round the top weight; normalize against the stored value. const auto minWeight = weights[argMin] / r.w; auto& min = p->e[argMin]; min.w = cn.lookup(minWeight); @@ -204,76 +261,65 @@ auto Edge::normalize(Node* p, const std::array& e, return r; } -template -auto Edge::getValueByIndex(const std::size_t i) const -> std::complex - requires IsVector -{ - const auto numQubits = isTerminal() ? 0U : static_cast(p->v) + 1U; +auto getValueByIndex(const vEdge& edge, const size_t i) -> std::complex { + const auto numQubits = + edge.isTerminal() ? 0U : static_cast(edge.p->v) + 1U; if (numQubits < std::numeric_limits::digits && (i >> numQubits) != 0U) { throw std::out_of_range("Vector index is out of range."); } - auto edge = *this; - auto amplitude = static_cast>(edge.w); - while (!edge.isTerminal()) { - const auto q = edge.p->v; + auto current = edge; + auto amplitude = static_cast>(current.w); + while (!current.isTerminal()) { + const auto q = current.p->v; const auto bit = q < std::numeric_limits::digits ? (i >> q) & 1U : 0U; - edge = edge.p->e[bit]; - amplitude *= static_cast>(edge.w); + current = current.p->e[bit]; + amplitude *= static_cast>(current.w); } return amplitude; } -template -auto Edge::getVector(const fp threshold) const -> CVec - requires IsVector -{ - if (isTerminal()) { - return {static_cast>(w)}; +auto getVector(const vEdge& edge, const fp threshold) -> CVec { + if (edge.isTerminal()) { + return {static_cast>(edge.w)}; } - const std::size_t dim = 2ULL << p->v; + const size_t dim = 2ULL << edge.p->v; auto vec = CVec(dim, 0.); traverseVector( - 1., 0, - [&vec](const std::size_t i, const std::complex& c) { vec.at(i) = c; }, + edge, 1., 0, + [&vec](const size_t i, const std::complex& c) { vec.at(i) = c; }, threshold); return vec; } -template -auto Edge::getSparseVector(const fp threshold) const -> SparseCVec - requires IsVector -{ - if (isTerminal()) { - return {{0, static_cast>(w)}}; +auto getSparseVector(const vEdge& edge, const fp threshold) -> SparseCVec { + if (edge.isTerminal()) { + return {{0, static_cast>(edge.w)}}; } auto vec = SparseCVec{}; traverseVector( - 1., 0, - [&vec](const std::size_t i, const std::complex& c) { vec[i] = c; }, + edge, 1., 0, + [&vec](const size_t i, const std::complex& c) { vec[i] = c; }, threshold); return vec; } -template -auto Edge::printVector() const -> void - requires IsVector -{ +auto printVector(const vEdge& edge) -> void { constexpr auto precision = 3; const auto oldPrecision = std::cout.precision(); std::cout << std::setprecision(precision); - if (isTerminal()) { - std::cout << "0: " << static_cast>(w) << "\n"; + if (edge.isTerminal()) { + std::cout << "0: " << static_cast>(edge.w) << "\n"; return; } - const std::size_t element = 2ULL << p->v; + const size_t element = 2ULL << edge.p->v; for (auto i = 0ULL; i < element; i++) { - const auto amplitude = getValueByIndex(i); - const auto n = static_cast(p->v) + 1U; + const auto amplitude = getValueByIndex(edge, i); + const auto n = static_cast(edge.p->v) + 1U; for (auto j = n; j > 0; --j) { std::cout << ((i >> (j - 1)) & 1ULL); } @@ -283,55 +329,25 @@ auto Edge::printVector() const -> void std::cout << std::flush; } -template -auto Edge::addToVector(CVec& amplitudes) const -> void - requires IsVector -{ - if (isTerminal()) { - amplitudes[0] += static_cast>(w); - return; - } - - traverseVector(1., 0, - [&litudes](const std::size_t i, const std::complex& c) { - amplitudes[i] += c; - }); -} - -template -void Edge::traverseVector(const std::complex& amp, - const std::size_t i, const AmplitudeFunc& f, - const fp threshold) const - requires IsVector -{ - const auto c = amp * static_cast>(w); - - if (threshold > 0. && std::abs(c) < threshold) { +auto addToVector(const vEdge& edge, CVec& amplitudes) -> void { + if (edge.isTerminal()) { + amplitudes[0] += static_cast>(edge.w); return; } - if (isTerminal()) { - f(i, c); - return; - } - - // recursive case - if (const auto& e = p->e[0]; !e.w.exactlyZero()) { - e.traverseVector(c, i, f, threshold); - } - if (const auto& e = p->e[1]; !e.w.exactlyZero()) { - e.traverseVector(c, i | (1ULL << p->v), f, threshold); - } + traverseVector( + edge, 1., 0, + [&litudes](const size_t i, const std::complex& c) { + amplitudes[i] += c; + }, + 0.); } -///----------------------------------------------------------------------------- -/// \n Methods for matrix DDs \n -///----------------------------------------------------------------------------- -template -auto Edge::normalize(Node* p, const std::array& e, - MemoryManager& mm, ComplexNumbers& cn) -> Edge - requires IsMatrix -{ +//----------------------------------------------------------------------------- +// \n Methods for matrix DDs \n +//----------------------------------------------------------------------------- +auto normalize(mNode* p, const std::array, NEDGE>& e, + MemoryManager& mm, ComplexNumbers& cn) -> Edge { assert(p != nullptr && "Node pointer passed to normalize is null."); const auto zero = std::array{ e[0].w.exactlyZero(), @@ -340,9 +356,9 @@ auto Edge::normalize(Node* p, const std::array& e, e[3].w.exactlyZero(), }; - if (std::all_of(zero.begin(), zero.end(), [](auto b) { return b; })) { + if (std::ranges::all_of(zero, [](auto b) { return b; })) { mm.returnEntry(*p); - return Edge::zero(); + return mEdge::zero(); } auto weights = std::array{ @@ -352,8 +368,8 @@ auto Edge::normalize(Node* p, const std::array& e, static_cast(e[3].w), }; - /// The incoming scale does not affect normalized coefficients. Remove it - /// before squared magnitudes and complex division can overflow or underflow. + // The incoming scale does not affect normalized coefficients. Remove it + // before squared magnitudes and complex division can overflow or underflow. fp maxComponent = 0.; for (const auto& w : weights) { maxComponent = std::max({maxComponent, std::abs(w.r), std::abs(w.i)}); @@ -365,13 +381,13 @@ auto Edge::normalize(Node* p, const std::array& e, } } - std::optional argMax = std::nullopt; + std::optional argMax = std::nullopt; fp maxMag2 = 0.; auto maxVal = Complex::one(); // determine max amplitude for (auto i = 0U; i < NEDGE; ++i) { if (zero[i]) { - p->e[i] = Edge::zero(); + p->e[i] = mEdge::zero(); continue; } const auto& w = weights[i]; @@ -397,106 +413,95 @@ auto Edge::normalize(Node* p, const std::array& e, continue; } if (i == argMaxValue) { - p->e[i] = {e[i].p, Complex::one()}; + p->e[i] = {.p = e[i].p, .w = Complex::one()}; continue; } - p->e[i] = {e[i].p, cn.lookup(weights[i] / argMaxWeight)}; + p->e[i] = {.p = e[i].p, .w = cn.lookup(weights[i] / argMaxWeight)}; if (p->e[i].w.exactlyZero()) { - p->e[i].p = Node::getTerminal(); + p->e[i].p = mNode::getTerminal(); } } - return Edge{p, maxVal}; + return mEdge{.p = p, .w = maxVal}; } -template -auto Edge::getValueByIndex(const std::size_t numQubits, - const std::size_t i, const std::size_t j) const - -> std::complex - requires IsMatrix -{ +auto getValueByIndex(const mEdge& edge, const size_t numQubits, const size_t i, + const size_t j) -> std::complex { if (numQubits < std::numeric_limits::digits && ((i >> numQubits) != 0U || (j >> numQubits) != 0U)) { throw std::out_of_range("Matrix index is out of range."); } - if (isTerminal()) { - return i == j ? static_cast>(w) : 0.; + if (edge.isTerminal()) { + return i == j ? static_cast>(edge.w) : 0.; } - auto edge = *this; - auto amplitude = static_cast>(edge.w); + auto current = edge; + auto amplitude = static_cast>(current.w); for (auto level = numQubits; level > 0; --level) { const auto q = level - 1; const auto rowBit = q < std::numeric_limits::digits ? (i >> q) & 1U : 0U; const auto colBit = q < std::numeric_limits::digits ? (j >> q) & 1U : 0U; - if (edge.isTerminal() || edge.p->v != q) { - if (edge.isZeroTerminal() || rowBit != colBit) { + if (current.isTerminal() || current.p->v != q) { + if (current.isZeroTerminal() || rowBit != colBit) { return 0.; } } else { - edge = edge.p->e[(2 * rowBit) + colBit]; - amplitude *= static_cast>(edge.w); + current = current.p->e[(2 * rowBit) + colBit]; + amplitude *= static_cast>(current.w); } } return amplitude; } -template -auto Edge::getMatrix(const std::size_t numQubits, - const fp threshold) const -> CMat - requires IsMatrix -{ +auto getMatrix(const mEdge& edge, const size_t numQubits, const fp threshold) + -> CMat { if (numQubits == 0U) { - return CMat{1, {static_cast>(w)}}; + return CMat{1, {static_cast>(edge.w)}}; } - const std::size_t dim = 1ULL << numQubits; + const size_t dim = 1ULL << numQubits; auto mat = CMat(dim, CVec(dim, 0.)); traverseMatrix( - 1, 0ULL, 0ULL, - [&mat](const std::size_t i, const std::size_t j, - const std::complex& c) { mat.at(i).at(j) = c; }, + edge, 1, 0ULL, 0ULL, + [&mat](const size_t i, const size_t j, const std::complex& c) { + mat.at(i).at(j) = c; + }, numQubits, threshold); return mat; } -template -auto Edge::getSparseMatrix(const std::size_t numQubits, - const fp threshold) const -> SparseCMat - requires IsMatrix -{ +auto getSparseMatrix(const mEdge& edge, const size_t numQubits, + const fp threshold) -> SparseCMat { if (numQubits == 0U) { - return {{{0U, 0U}, static_cast>(w)}}; + return {{{0U, 0U}, static_cast>(edge.w)}}; } auto mat = SparseCMat{}; traverseMatrix( - 1, 0ULL, 0ULL, - [&mat](const std::size_t i, const std::size_t j, - const std::complex& c) { mat[{i, j}] = c; }, + edge, 1, 0ULL, 0ULL, + [&mat](const size_t i, const size_t j, const std::complex& c) { + mat[{i, j}] = c; + }, numQubits, threshold); return mat; } -template -auto Edge::printMatrix(const std::size_t numQubits) const -> void - requires IsMatrix -{ +auto printMatrix(const mEdge& edge, const size_t numQubits) -> void { constexpr auto precision = 3; const auto oldPrecision = std::cout.precision(); std::cout << std::setprecision(precision); if (numQubits == 0U) { - std::cout << static_cast>(w) << "\n"; + std::cout << static_cast>(edge.w) << "\n"; return; } - assert(isTerminal() || numQubits > p->v); - const std::size_t element = 1ULL << numQubits; + assert(edge.isTerminal() || numQubits > edge.p->v); + const size_t element = 1ULL << numQubits; for (auto i = 0ULL; i < element; ++i) { for (auto j = 0ULL; j < element; ++j) { - const auto amplitude = getValueByIndex(numQubits, i, j); + const auto amplitude = getValueByIndex(edge, numQubits, i, j); std::cout << amplitude << " "; } std::cout << "\n"; @@ -505,66 +510,26 @@ auto Edge::printMatrix(const std::size_t numQubits) const -> void std::cout << std::flush; } -template -void Edge::traverseMatrix(const std::complex& amp, - const std::size_t i, const std::size_t j, - MatrixEntryFunc f, const std::size_t level, - const fp threshold) const - requires IsMatrix -{ - traverseMatrixImpl(amp, i, j, f, level, threshold); -} - -template -void Edge::traverseMatrixImpl(const std::complex& amp, - const std::size_t i, const std::size_t j, - const MatrixEntryFunc& f, - const std::size_t level, - const fp threshold) const - requires IsMatrix -{ - const auto c = amp * static_cast>(w); - - if (threshold > 0. && std::abs(c) < threshold) { - return; - } - - if (level == 0) { - assert(isTerminal()); - f(i, j, c); - return; - } - - const auto nextLevel = static_cast(level - 1U); - const std::size_t x = i | (1ULL << nextLevel); - const std::size_t y = j | (1ULL << nextLevel); - if (isTerminal() || p->v < nextLevel) { - traverseMatrixImpl(amp, i, j, f, nextLevel, threshold); - traverseMatrixImpl(amp, x, y, f, nextLevel, threshold); - return; - } - - const auto coords = {std::pair{i, j}, {i, y}, {x, j}, {x, y}}; - std::size_t k = 0U; - for (const auto& [a, b] : coords) { - if (auto const& e = p->e[k++]; !e.w.exactlyZero()) { - e.traverseMatrixImpl(c, a, b, f, nextLevel, threshold); - } - } +void traverseMatrix(const mEdge& edge, const std::complex& amp, + const size_t i, const size_t j, + // Keep one callback copy at the public traversal boundary. + // NOLINTNEXTLINE(performance-unnecessary-value-param) + MatrixEntryFunc f, const size_t level, const fp threshold) { + traverseMatrixImpl(edge, amp, i, j, f, level, threshold); } -///----------------------------------------------------------------------------- -/// \n Explicit instantiations \n -///----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// \n Explicit instantiations \n +//----------------------------------------------------------------------------- template struct Edge; template struct Edge; } // namespace dd -///----------------------------------------------------------------------------- -/// \n Hash related code \n -///----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// \n Hash related code \n +//----------------------------------------------------------------------------- template auto std::hash>::operator()( diff --git a/src/dd/Package.cpp b/src/dd/Package.cpp index 0811307179..c3c6466274 100644 --- a/src/dd/Package.cpp +++ b/src/dd/Package.cpp @@ -1031,7 +1031,7 @@ mEdge Package::partialTrace(const mEdge& a, return cn.lookup(r); } ComplexValue Package::trace(const mEdge& a, const std::size_t numQubits) { - if (a.isIdentity()) { + if (dd::isIdentity(a)) { return static_cast(a.w); } std::vector eliminatedBelow(numQubits + 1); @@ -1051,7 +1051,7 @@ mCachedEdge Package::trace(const mEdge& a, if (aWeight.exactlyZero()) { return mCachedEdge::zero(); } - if (a.isIdentity()) { + if (dd::isIdentity(a)) { return {a.p, aWeight}; } @@ -1173,7 +1173,7 @@ mEdge Package::reduceAncillae(mEdge e, const std::vector& ancillary, return e; } - if (e.isIdentity()) { + if (dd::isIdentity(e)) { auto g = e; for (auto i = 0U; i < ancillary.size(); ++i) { if (ancillary[i]) { @@ -1250,7 +1250,7 @@ mEdge Package::reduceGarbage(const mEdge& e, const std::vector& garbage, return e; } - if (e.isIdentity()) { + if (dd::isIdentity(e)) { auto g = e; for (auto i = 0U; i < garbage.size(); ++i) { if (garbage[i]) { @@ -1331,7 +1331,7 @@ mCachedEdge Package::reduceAncillaeRecursion(mNode* p, continue; } - if (p->e[i].isIdentity()) { + if (dd::isIdentity(p->e[i])) { auto g = mCachedEdge::one(); for (auto j = lowerbound; j < p->v; ++j) { if (ancillary[j]) { @@ -1460,7 +1460,7 @@ mCachedEdge Package::reduceGarbageRecursion(mNode* p, continue; } - if (p->e[i].isIdentity()) { + if (dd::isIdentity(p->e[i])) { edges[i] = mCachedEdge::one(); for (auto j = lowerbound; j < p->v; ++j) { if (garbage[j]) { diff --git a/src/qdmi/devices/dd/Device.cpp b/src/qdmi/devices/dd/Device.cpp index 6256c5eb10..5caa8f4acf 100644 --- a/src/qdmi/devices/dd/Device.cpp +++ b/src/qdmi/devices/dd/Device.cpp @@ -14,6 +14,7 @@ #include "qdmi/devices/dd/Device.hpp" #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Package.hpp" #include "mqt/Compiler/Programs.h" #include "mqt/Dialect/MQT/IR/MQTDialect.h" @@ -809,7 +810,7 @@ auto MQT_DDSIM_QDMI_Device_Job_impl_d::getStateVector(const size_t size, return QDMI_ERROR_OUTOFMEM; } std::call_once(stateVecOnce_, - [this] { stateVec_ = stateVecDD_.getVector(); }); + [this] { stateVec_ = dd::getVector(stateVecDD_); }); std::memcpy(data, stateVec_.data(), reqSize); } if (sizeRet != nullptr) { @@ -827,7 +828,7 @@ auto MQT_DDSIM_QDMI_Device_Job_impl_d::getSparseResults( return QDMI_ERROR_NOTSUPPORTED; } std::call_once(stateVecSparseOnce_, [this] { - const auto sparse = stateVecDD_.getSparseVector(); + const auto sparse = dd::getSparseVector(stateVecDD_); stateVecSparse_.assign(sparse.begin(), sparse.end()); std::ranges::sort(stateVecSparse_, {}, &decltype(stateVecSparse_)::value_type::first); @@ -922,7 +923,7 @@ auto MQT_DDSIM_QDMI_Device_Job_impl_d::getProbabilities(const size_t size, return QDMI_ERROR_OUTOFMEM; } std::call_once(stateVecOnce_, - [this] { stateVec_ = stateVecDD_.getVector(); }); + [this] { stateVec_ = dd::getVector(stateVecDD_); }); // NOLINTNEXTLINE(misc-const-correctness): fills a mutable output buffer. auto* dataPtr = static_cast(data); for (const auto& c : stateVec_) { diff --git a/test/dd/test_dd_functionality.cpp b/test/dd/test_dd_functionality.cpp index e8bf2b2f4f..cfc140ccea 100644 --- a/test/dd/test_dd_functionality.cpp +++ b/test/dd/test_dd_functionality.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Node.hpp" #include "dd/Operations.hpp" #include "dd/Package.hpp" @@ -30,7 +31,7 @@ TEST(DDGateConstruction, AppliesGlobalPhase) { const auto phased = applyGlobalPhase(state, std::numbers::pi / 2., package); EXPECT_EQ(state, phased); - const auto vector = phased.getVector(); + const auto vector = dd::getVector(phased); ASSERT_EQ(vector.size(), 2); EXPECT_NEAR(vector[0].real(), 0., RealNumber::eps); @@ -39,7 +40,7 @@ TEST(DDGateConstruction, AppliesGlobalPhase) { package.garbageCollect(true); state = package.applyOperation(Package::makeIdent(), state); - EXPECT_EQ(state.getVector(), vector); + EXPECT_EQ(dd::getVector(state), vector); package.decRef(state); package.garbageCollect(true); const auto [vectors, matrices, reals] = package.computeActiveCounts(); @@ -53,7 +54,7 @@ TEST(DDGateConstruction, ScalarGlobalPhaseSurvivesCollection) { auto state = vEdge::one(); applyGlobalPhase(state, 0.3, package); package.garbageCollect(true); - EXPECT_NEAR(std::abs(state.getVector().front() - std::polar(1., 0.3)), 0., + EXPECT_NEAR(std::abs(dd::getVector(state).front() - std::polar(1., 0.3)), 0., RealNumber::eps); package.decRef(state); package.garbageCollect(true); diff --git a/test/dd/test_edge_functionality.cpp b/test/dd/test_edge_functionality.cpp index 08fe9a1462..fa7411fb28 100644 --- a/test/dd/test_edge_functionality.cpp +++ b/test/dd/test_edge_functionality.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Node.hpp" #include "dd/Package.hpp" #include "dd/RealNumber.hpp" @@ -37,8 +38,8 @@ TEST(VectorFunctionality, GetValueByPathTerminal) { } TEST(VectorFunctionality, GetValueByIndexTerminal) { - EXPECT_EQ(vEdge::zero().getValueByIndex(0), 0.); - EXPECT_EQ(vEdge::one().getValueByIndex(0), 1.); + EXPECT_EQ(dd::getValueByIndex(vEdge::zero(), 0), 0.); + EXPECT_EQ(dd::getValueByIndex(vEdge::one(), 0), 1.); } TEST(VectorFunctionality, GetValueByIndexEndianness) { @@ -52,7 +53,7 @@ TEST(VectorFunctionality, GetValueByIndexEndianness) { const auto stateDD = makeStateFromVector(state, *dd); for (std::size_t i = 0U; i < state.size(); ++i) { - EXPECT_EQ(state[i], stateDD.getValueByIndex(i)); + EXPECT_EQ(state[i], dd::getValueByIndex(stateDD, i)); } } @@ -61,12 +62,13 @@ TEST(VectorFunctionality, WideIndices) { auto dd = std::make_unique(digits + 1U); const auto ones = makeBasisState(digits, std::vector(digits, true), *dd); - EXPECT_EQ(ones.getValueByIndex(std::numeric_limits::max()), 1.); + EXPECT_EQ(dd::getValueByIndex(ones, std::numeric_limits::max()), 1.); const auto zero = makeZeroState(digits + 1U, *dd); - EXPECT_EQ(zero.getValueByIndex(0), 1.); - EXPECT_EQ(zero.getValueByIndex(std::numeric_limits::max()), 0.); - EXPECT_THROW(vEdge::one().getValueByIndex(1), std::out_of_range); - EXPECT_THROW(makeZeroState(3, *dd).getValueByIndex(8), std::out_of_range); + EXPECT_EQ(dd::getValueByIndex(zero, 0), 1.); + EXPECT_EQ(dd::getValueByIndex(zero, std::numeric_limits::max()), 0.); + EXPECT_THROW(dd::getValueByIndex(vEdge::one(), 1), std::out_of_range); + EXPECT_THROW(dd::getValueByIndex(makeZeroState(3, *dd), 8), + std::out_of_range); } TEST(VectorFunctionality, InvalidPaths) { @@ -83,18 +85,20 @@ TEST(MatrixFunctionality, WideIndices) { constexpr auto digits = std::numeric_limits::digits; auto dd = std::make_unique(digits + 1U); const auto gate = dd->makeGateDD(GateMatrix{0., {0., -1.}, {0., 1.}, 0.}, 0); - EXPECT_EQ(gate.getValueByIndex(digits + 1U, 0, 1), std::complex(0, -1)); - EXPECT_EQ(gate.getValueByIndex(digits + 1U, 1, 0), std::complex(0, 1)); - EXPECT_EQ(gate.getValueByIndex(digits + 1U, 2, 1), 0.); + EXPECT_EQ(dd::getValueByIndex(gate, digits + 1U, 0, 1), + std::complex(0, -1)); + EXPECT_EQ(dd::getValueByIndex(gate, digits + 1U, 1, 0), + std::complex(0, 1)); + EXPECT_EQ(dd::getValueByIndex(gate, digits + 1U, 2, 1), 0.); const auto highGate = dd->makeGateDD(GateMatrix{0., {0., -1.}, {0., 1.}, 0.}, digits); - EXPECT_EQ(highGate.getValueByIndex(digits + 1U, 0, 0), 0.); - EXPECT_EQ(mEdge::one().getValueByIndex(digits, - std::numeric_limits::max(), - std::numeric_limits::max()), + EXPECT_EQ(dd::getValueByIndex(highGate, digits + 1U, 0, 0), 0.); + EXPECT_EQ(dd::getValueByIndex(mEdge::one(), digits, + std::numeric_limits::max(), + std::numeric_limits::max()), 1.); - EXPECT_THROW(gate.getValueByIndex(1, 2, 0), std::out_of_range); - EXPECT_THROW(mEdge::one().getValueByIndex(1, 0, 2), std::out_of_range); + EXPECT_THROW(dd::getValueByIndex(gate, 1, 2, 0), std::out_of_range); + EXPECT_THROW(dd::getValueByIndex(mEdge::one(), 1, 0, 2), std::out_of_range); } TEST(MatrixFunctionality, InvalidPaths) { @@ -111,16 +115,18 @@ TEST(EdgeFunctionality, NonpositiveExportThresholds) { const auto matrix = dd->makeGateDD(GateMatrix{0., {0., -1.}, {0., 1.}, 0.}, 0); for (const auto threshold : {0., -1., std::numeric_limits::quiet_NaN()}) { - EXPECT_EQ(vector.getVector(threshold), vector.getVector()); - EXPECT_EQ(vector.getSparseVector(threshold), vector.getSparseVector()); - EXPECT_EQ(matrix.getMatrix(1, threshold), matrix.getMatrix(1)); - EXPECT_EQ(matrix.getSparseMatrix(1, threshold), matrix.getSparseMatrix(1)); + EXPECT_EQ(dd::getVector(vector, threshold), dd::getVector(vector)); + EXPECT_EQ(dd::getSparseVector(vector, threshold), + dd::getSparseVector(vector)); + EXPECT_EQ(dd::getMatrix(matrix, 1, threshold), dd::getMatrix(matrix, 1)); + EXPECT_EQ(dd::getSparseMatrix(matrix, 1, threshold), + dd::getSparseMatrix(matrix, 1)); } } TEST(VectorFunctionality, GetVectorTerminal) { - EXPECT_EQ(vEdge::zero().getVector(), CVec{0.}); - EXPECT_EQ(vEdge::one().getVector(), CVec{1.}); + EXPECT_EQ(dd::getVector(vEdge::zero()), CVec{0.}); + EXPECT_EQ(dd::getVector(vEdge::one()), CVec{1.}); } TEST(VectorFunctionality, GetVectorRoundtrip) { @@ -132,7 +138,7 @@ TEST(VectorFunctionality, GetVectorRoundtrip) { std::sqrt(0.4), }; const auto stateDD = makeStateFromVector(state, *dd); - const auto stateVec = stateDD.getVector(); + const auto stateVec = dd::getVector(stateDD); EXPECT_EQ(stateVec, state); } @@ -145,18 +151,19 @@ TEST(VectorFunctionality, GetVectorTolerance) { std::sqrt(0.4), }; const auto stateDD = makeStateFromVector(state, *dd); - const auto stateVec = stateDD.getVector(std::sqrt(0.1)); + const auto stateVec = dd::getVector(stateDD, std::sqrt(0.1)); EXPECT_EQ(stateVec, state); - const auto stateVec2 = stateDD.getVector(std::sqrt(0.1) + RealNumber::eps); + const auto stateVec2 = + dd::getVector(stateDD, std::sqrt(0.1) + RealNumber::eps); EXPECT_NE(stateVec2, state); EXPECT_EQ(stateVec2[0], 0.); } TEST(VectorFunctionality, GetSparseVectorTerminal) { const auto zero = SparseCVec{{0, 0}}; - EXPECT_EQ(vEdge::zero().getSparseVector(), zero); + EXPECT_EQ(dd::getSparseVector(vEdge::zero()), zero); const auto one = SparseCVec{{0, 1}}; - EXPECT_EQ(vEdge::one().getSparseVector(), one); + EXPECT_EQ(dd::getSparseVector(vEdge::one()), one); } TEST(VectorFunctionality, GetSparseVectorConsistency) { @@ -168,8 +175,8 @@ TEST(VectorFunctionality, GetSparseVectorConsistency) { std::sqrt(0.4), }; const auto stateDD = makeStateFromVector(state, *dd); - const auto stateSparseVec = stateDD.getSparseVector(); - const auto stateVec = stateDD.getVector(); + const auto stateSparseVec = dd::getSparseVector(stateDD); + const auto stateVec = dd::getVector(stateDD); for (const auto& [index, value] : stateSparseVec) { EXPECT_EQ(value, stateVec[index]); } @@ -184,23 +191,23 @@ TEST(VectorFunctionality, GetSparseVectorTolerance) { std::sqrt(0.4), }; const auto stateDD = makeStateFromVector(state, *dd); - const auto stateSparseVec = stateDD.getSparseVector(std::sqrt(0.1)); + const auto stateSparseVec = dd::getSparseVector(stateDD, std::sqrt(0.1)); for (const auto& [index, value] : stateSparseVec) { EXPECT_EQ(value, state[index]); } const auto stateSparseVec2 = - stateDD.getSparseVector(std::sqrt(0.1) + RealNumber::eps); + dd::getSparseVector(stateDD, std::sqrt(0.1) + RealNumber::eps); EXPECT_NE(stateSparseVec2, stateSparseVec); EXPECT_EQ(stateSparseVec2.count(0), 0); } TEST(VectorFunctionality, PrintVectorTerminal) { testing::internal::CaptureStdout(); - vEdge::zero().printVector(); + dd::printVector(vEdge::zero()); const auto zeroStr = testing::internal::GetCapturedStdout(); EXPECT_EQ(zeroStr, "0: (0,0)\n"); testing::internal::CaptureStdout(); - vEdge::one().printVector(); + dd::printVector(vEdge::one()); const auto oneStr = testing::internal::GetCapturedStdout(); EXPECT_EQ(oneStr, "0: (1,0)\n"); } @@ -215,7 +222,7 @@ TEST(VectorFunctionality, PrintVector) { }; const auto stateDD = makeStateFromVector(state, *dd); testing::internal::CaptureStdout(); - stateDD.printVector(); + dd::printVector(stateDD); const auto stateStr = testing::internal::GetCapturedStdout(); EXPECT_EQ(stateStr, "00: (0.316,0)\n01: (0.447,0)\n10: (0.548,0)\n11: (0.632,0)\n"); @@ -223,7 +230,7 @@ TEST(VectorFunctionality, PrintVector) { TEST(VectorFunctionality, AddToVectorTerminal) { CVec vec = {0.}; - vEdge::one().addToVector(vec); + dd::addToVector(vEdge::one(), vec); EXPECT_EQ(vec, CVec{1.}); } @@ -238,7 +245,7 @@ TEST(VectorFunctionality, AddToVector) { std::sqrt(0.4), }; const auto stateDD = makeStateFromVector(state, *dd); - stateDD.addToVector(vec); + dd::addToVector(stateDD, vec); EXPECT_EQ(vec, state); } @@ -264,8 +271,8 @@ TEST(MatrixFunctionality, GetValueByPathTerminal) { } TEST(MatrixFunctionality, GetValueByIndexTerminal) { - EXPECT_EQ(mEdge::zero().getValueByIndex(0, 0, 0), 0.); - EXPECT_EQ(mEdge::one().getValueByIndex(0, 0, 0), 1.); + EXPECT_EQ(dd::getValueByIndex(mEdge::zero(), 0, 0, 0), 0.); + EXPECT_EQ(dd::getValueByIndex(mEdge::one(), 0, 0, 0), 1.); } TEST(MatrixFunctionality, ScalarAccessPreservesImplicitIdentities) { @@ -274,7 +281,7 @@ TEST(MatrixFunctionality, ScalarAccessPreservesImplicitIdentities) { auto gate = package.makeGateDD(GateMatrix{0., 1., 1., 0.}, 1); gate.w = phase; for (const auto& matrix : {mEdge::zero(), mEdge::terminal(phase), gate}) { - const auto dense = matrix.getMatrix(3); + const auto dense = dd::getMatrix(matrix, 3); for (size_t row = 0; row < dense.size(); ++row) { for (size_t col = 0; col < dense.size(); ++col) { std::string path(3, '0'); @@ -282,7 +289,7 @@ TEST(MatrixFunctionality, ScalarAccessPreservesImplicitIdentities) { path[bit] = static_cast('0' + (2 * ((row >> bit) & 1U)) + ((col >> bit) & 1U)); } - EXPECT_EQ(matrix.getValueByIndex(3, row, col), dense[row][col]); + EXPECT_EQ(dd::getValueByIndex(matrix, 3, row, col), dense[row][col]); EXPECT_EQ(matrix.getValueByPath(3, path), dense[row][col]); } } @@ -303,7 +310,7 @@ TEST(MatrixFunctionality, GetValueByIndexEndianness) { for (std::size_t i = 0U; i < mat.size(); ++i) { for (std::size_t j = 0U; j < mat.size(); ++j) { - const auto val = matDD.getValueByIndex(dd->qubits(), i, j); + const auto val = dd::getValueByIndex(matDD, dd->qubits(), i, j); const auto ref = mat[i][j]; EXPECT_NEAR(ref.real(), val.real(), 1e-10); EXPECT_NEAR(ref.imag(), val.imag(), 1e-10); @@ -312,8 +319,8 @@ TEST(MatrixFunctionality, GetValueByIndexEndianness) { } TEST(MatrixFunctionality, GetMatrixTerminal) { - EXPECT_EQ(mEdge::zero().getMatrix(0), CMat{{0.}}); - EXPECT_EQ(mEdge::one().getMatrix(0), CMat{{1.}}); + EXPECT_EQ(dd::getMatrix(mEdge::zero(), 0), CMat{{0.}}); + EXPECT_EQ(dd::getMatrix(mEdge::one(), 0), CMat{{1.}}); } TEST(MatrixFunctionality, GetMatrixRoundtrip) { @@ -327,10 +334,10 @@ TEST(MatrixFunctionality, GetMatrixRoundtrip) { // clang-format on const auto matDD = dd->makeDDFromMatrix(mat); - const auto matVec = matDD.getMatrix(dd->qubits()); + const auto matVec = dd::getMatrix(matDD, dd->qubits()); for (std::size_t i = 0U; i < mat.size(); ++i) { for (std::size_t j = 0U; j < mat.size(); ++j) { - const auto val = matDD.getValueByIndex(dd->qubits(), i, j); + const auto val = dd::getValueByIndex(matDD, dd->qubits(), i, j); const auto ref = mat[i][j]; EXPECT_NEAR(ref.real(), val.real(), 1e-10); EXPECT_NEAR(ref.imag(), val.imag(), 1e-10); @@ -349,17 +356,17 @@ TEST(MatrixFunctionality, GetMatrixTolerance) { // clang-format on const auto matDD = dd->makeDDFromMatrix(mat); - const auto matVec = matDD.getMatrix(dd->qubits(), std::sqrt(0.1)); + const auto matVec = dd::getMatrix(matDD, dd->qubits(), std::sqrt(0.1)); for (std::size_t i = 0U; i < mat.size(); ++i) { for (std::size_t j = 0U; j < mat.size(); ++j) { - const auto val = matDD.getValueByIndex(dd->qubits(), i, j); + const auto val = dd::getValueByIndex(matDD, dd->qubits(), i, j); const auto ref = mat[i][j]; EXPECT_NEAR(ref.real(), val.real(), 1e-10); EXPECT_NEAR(ref.imag(), val.imag(), 1e-10); } } const auto matVec2 = - matDD.getMatrix(dd->qubits(), std::sqrt(0.1) + RealNumber::eps); + dd::getMatrix(matDD, dd->qubits(), std::sqrt(0.1) + RealNumber::eps); EXPECT_NE(matVec2, matVec); EXPECT_EQ(matVec2[0][0], 0.); EXPECT_EQ(matVec2[1][3], 0.); @@ -369,9 +376,9 @@ TEST(MatrixFunctionality, GetMatrixTolerance) { TEST(MatrixFunctionality, GetSparseMatrixTerminal) { const auto zero = SparseCMat{{{0, 0}, 0.}}; - EXPECT_EQ(mEdge::zero().getSparseMatrix(0), zero); + EXPECT_EQ(dd::getSparseMatrix(mEdge::zero(), 0), zero); const auto one = SparseCMat{{{0, 0}, 1.}}; - EXPECT_EQ(mEdge::one().getSparseMatrix(0), one); + EXPECT_EQ(dd::getSparseMatrix(mEdge::one(), 0), one); } TEST(MatrixFunctionality, GetSparseMatrixConsistency) { @@ -385,8 +392,8 @@ TEST(MatrixFunctionality, GetSparseMatrixConsistency) { // clang-format on const auto matDD = dd->makeDDFromMatrix(mat); - const auto matSparse = matDD.getSparseMatrix(dd->qubits()); - const auto matDense = matDD.getMatrix(dd->qubits()); + const auto matSparse = dd::getSparseMatrix(matDD, dd->qubits()); + const auto matDense = dd::getMatrix(matDD, dd->qubits()); for (const auto& [index, value] : matSparse) { const auto val = matDense.at(index.first).at(index.second); EXPECT_NEAR(value.real(), val.real(), 1e-10); @@ -405,15 +412,16 @@ TEST(MatrixFunctionality, GetSparseMatrixTolerance) { // clang-format on const auto matDD = dd->makeDDFromMatrix(mat); - const auto matSparse = matDD.getSparseMatrix(dd->qubits(), std::sqrt(0.1)); - const auto matDense = matDD.getMatrix(dd->qubits()); + const auto matSparse = + dd::getSparseMatrix(matDD, dd->qubits(), std::sqrt(0.1)); + const auto matDense = dd::getMatrix(matDD, dd->qubits()); for (const auto& [index, value] : matSparse) { const auto val = matDense.at(index.first).at(index.second); EXPECT_NEAR(value.real(), val.real(), 1e-10); EXPECT_NEAR(value.imag(), val.imag(), 1e-10); } - const auto matSparse2 = - matDD.getSparseMatrix(dd->qubits(), std::sqrt(0.1) + RealNumber::eps); + const auto matSparse2 = dd::getSparseMatrix(matDD, dd->qubits(), + std::sqrt(0.1) + RealNumber::eps); EXPECT_NE(matSparse2, matSparse); EXPECT_EQ(matSparse2.count({0, 0}), 0); EXPECT_EQ(matSparse2.count({1, 3}), 0); @@ -423,11 +431,11 @@ TEST(MatrixFunctionality, GetSparseMatrixTolerance) { TEST(MatrixFunctionality, PrintMatrixTerminal) { testing::internal::CaptureStdout(); - mEdge::zero().printMatrix(0); + dd::printMatrix(mEdge::zero(), 0); const auto zeroStr = testing::internal::GetCapturedStdout(); EXPECT_EQ(zeroStr, "(0,0)\n"); testing::internal::CaptureStdout(); - mEdge::one().printMatrix(0); + dd::printMatrix(mEdge::one(), 0); const auto oneStr = testing::internal::GetCapturedStdout(); EXPECT_EQ(oneStr, "(1,0)\n"); } @@ -444,7 +452,7 @@ TEST(MatrixFunctionality, PrintMatrix) { const auto matDD = dd->makeDDFromMatrix(mat); testing::internal::CaptureStdout(); - matDD.printMatrix(dd->qubits()); + dd::printMatrix(matDD, dd->qubits()); const auto matStr = testing::internal::GetCapturedStdout(); EXPECT_EQ(matStr, "(0.316,-0) (0.447,-0) (0.548,0) (0.632,0) \n" "(-0.447,0) (-0.548,0) (0.632,0) (0.316,0) \n" @@ -454,8 +462,8 @@ TEST(MatrixFunctionality, PrintMatrix) { TEST(MatrixFunctionality, TraversalUsesOneCallbackAcrossIdentityLevels) { size_t visited = 0; - mEdge::one().traverseMatrix( - {0., 1.}, 0, 0, + dd::traverseMatrix( + mEdge::one(), {0., 1.}, 0, 0, [&visited, ordinal = size_t{0}](const size_t i, const size_t j, const std::complex& amplitude) mutable { diff --git a/test/dd/test_package.cpp b/test/dd/test_package.cpp index ac31895222..c684fabf23 100644 --- a/test/dd/test_package.cpp +++ b/test/dd/test_package.cpp @@ -283,7 +283,7 @@ TEST(DDPackageTest, BellState) { auto const zeroState = makeZeroState(2, *dd); auto const bellState = dd->multiply(dd->multiply(cxGate, hGate), zeroState); - bellState.printVector(); + dd::printVector(bellState); // repeated calculation is practically for free auto const bellState2 = dd->multiply(dd->multiply(cxGate, hGate), zeroState); @@ -294,13 +294,13 @@ TEST(DDPackageTest, BellState) { ASSERT_EQ(bellState.getValueByPath(dd->qubits(), "10"), 0.); ASSERT_EQ(bellState.getValueByPath(dd->qubits(), "11"), SQRT2_2); - ASSERT_EQ(bellState.getValueByIndex(0), SQRT2_2); - ASSERT_EQ(bellState.getValueByIndex(1), 0.); - ASSERT_EQ(bellState.getValueByIndex(2), 0.); - ASSERT_EQ(bellState.getValueByIndex(3), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellState, 0), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellState, 1), 0.); + ASSERT_EQ(dd::getValueByIndex(bellState, 2), 0.); + ASSERT_EQ(dd::getValueByIndex(bellState, 3), SQRT2_2); auto const goalState = CVec{{SQRT2_2, 0.}, {0., 0.}, {0., 0.}, {SQRT2_2, 0.}}; - ASSERT_EQ(bellState.getVector(), goalState); + ASSERT_EQ(dd::getVector(bellState), goalState); ASSERT_DOUBLE_EQ(dd->fidelity(zeroState, bellState), 0.5); @@ -333,12 +333,12 @@ TEST(DDPackageTest, QFTState) { qftOp = dd->multiply(swapGate, qftOp); auto const qftState = dd->multiply(qftOp, makeZeroState(3, *dd)); - qftState.printVector(); + dd::printVector(qftState); for (size_t qubit = 0; qubit < 7; ++qubit) { - ASSERT_NEAR(qftState.getValueByIndex(qubit).real(), 0.5 * SQRT2_2, + ASSERT_NEAR(dd::getValueByIndex(qftState, qubit).real(), 0.5 * SQRT2_2, RealNumber::eps); - ASSERT_EQ(qftState.getValueByIndex(qubit).imag(), 0); + ASSERT_EQ(dd::getValueByIndex(qftState, qubit).imag(), 0); } checkDotOptions(qftState); @@ -377,7 +377,7 @@ TEST(DDPackageTest, NegativeControl) { getDD(TestGate(Control{1, Control::Type::Neg}, 0, Fixture::X), *dd); auto const zeroState = makeZeroState(2, *dd); auto const state01 = dd->multiply(xGate, zeroState); - EXPECT_EQ(state01.getValueByIndex(0b01).real(), 1.); + EXPECT_EQ(dd::getValueByIndex(state01, 0b01).real(), 1.); } TEST(DDPackageTest, IdentityTrace) { @@ -427,7 +427,7 @@ TEST(DDPackageTest, PartialTraceMatchesDenseOracleAcrossIdentityLevels) { const auto zx = package.multiply(package.makeGateDD(Z_MAT, 2), x); for (const auto& matrix : {Package::makeIdent(), x, zx, package.makeDDFromMatrix(input)}) { - const auto dense = matrix.getMatrix(3); + const auto dense = dd::getMatrix(matrix, 3); const auto fullTrace = package.trace(matrix, 3); for (size_t mask = 0; mask < 8; ++mask) { SCOPED_TRACE(mask); @@ -454,7 +454,7 @@ TEST(DDPackageTest, PartialTraceMatchesDenseOracleAcrossIdentityLevels) { } const auto result = package.partialTrace( matrix, {(mask & 1U) != 0, (mask & 2U) != 0, (mask & 4U) != 0}); - const auto actual = result.getMatrix(3 - removed); + const auto actual = dd::getMatrix(result, 3 - removed); for (size_t row = 0; row < dimension; ++row) { for (size_t col = 0; col < dimension; ++col) { EXPECT_NEAR(std::abs(actual[row][col] - expected[row][col]), 0., @@ -627,39 +627,39 @@ TEST(DDPackageTest, BellMatrix) { auto const bellMatrix = dd->multiply(cxGate, hGate); - bellMatrix.printMatrix(dd->qubits()); + dd::printMatrix(bellMatrix, dd->qubits()); ASSERT_EQ(bellMatrix.getValueByPath(dd->qubits(), "00"), SQRT2_2); ASSERT_EQ(bellMatrix.getValueByPath(dd->qubits(), "02"), 0.); ASSERT_EQ(bellMatrix.getValueByPath(dd->qubits(), "20"), 0.); ASSERT_EQ(bellMatrix.getValueByPath(dd->qubits(), "22"), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 0, 0), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 1, 0), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 2, 0), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 3, 0), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 0, 0), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 1, 0), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 2, 0), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 3, 0), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 0, 1), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 1, 1), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 2, 1), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 3, 1), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 0, 1), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 1, 1), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 2, 1), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 3, 1), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 0, 2), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 1, 2), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 2, 2), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 3, 2), -SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 0, 2), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 1, 2), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 2, 2), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 3, 2), -SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 0, 3), 0.); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 1, 3), SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 2, 3), -SQRT2_2); - ASSERT_EQ(bellMatrix.getValueByIndex(dd->qubits(), 3, 3), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 0, 3), 0.); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 1, 3), SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 2, 3), -SQRT2_2); + ASSERT_EQ(dd::getValueByIndex(bellMatrix, dd->qubits(), 3, 3), 0.); auto const goalRow0 = CVec{{SQRT2_2, 0.}, {0., 0.}, {SQRT2_2, 0.}, {0., 0.}}; auto const goalRow1 = CVec{{0., 0.}, {SQRT2_2, 0.}, {0., 0.}, {SQRT2_2, 0.}}; auto const goalRow2 = CVec{{0., 0.}, {SQRT2_2, 0.}, {0., 0.}, {-SQRT2_2, 0.}}; auto const goalRow3 = CVec{{SQRT2_2, 0.}, {0., 0.}, {-SQRT2_2, 0.}, {0., 0.}}; auto const goalMatrix = CMat{goalRow0, goalRow1, goalRow2, goalRow3}; - ASSERT_EQ(bellMatrix.getMatrix(dd->qubits()), goalMatrix); + ASSERT_EQ(dd::getMatrix(bellMatrix, dd->qubits()), goalMatrix); checkDotOptions(bellMatrix); checkDotFile(bellMatrix, "bell_matrix.dot"); @@ -787,7 +787,7 @@ TEST(DDPackageTest, GarbageVector) { auto const zeroState = makeZeroState(2, *dd); auto bellState = dd->multiply(dd->multiply(cxGate, hGate), zeroState); std::cout << "Bell State:\n"; - bellState.printVector(); + dd::printVector(bellState); dd->incRef(bellState); auto reducedBellState = @@ -799,17 +799,17 @@ TEST(DDPackageTest, GarbageVector) { dd->incRef(bellState); reducedBellState = dd->reduceGarbage(bellState, {false, true, false, false}); - auto vec = reducedBellState.getVector(); + auto vec = dd::getVector(reducedBellState); std::cout << "Reduced Bell State (q1 garbage):\n"; - reducedBellState.printVector(); + dd::printVector(reducedBellState); EXPECT_EQ(vec[2], 0.); EXPECT_EQ(vec[3], 0.); dd->incRef(bellState); reducedBellState = dd->reduceGarbage(bellState, {true, false, false, false}); std::cout << "Reduced Bell State (q0 garbage):\n"; - reducedBellState.printVector(); - vec = reducedBellState.getVector(); + dd::printVector(reducedBellState); + vec = dd::getVector(reducedBellState); EXPECT_EQ(vec[1], 0.); EXPECT_EQ(vec[3], 0.); } @@ -832,7 +832,7 @@ TEST(DDPackageTest, GarbageMatrix) { dd->incRef(bellMatrix); reducedBellMatrix = dd->reduceGarbage(bellMatrix, {false, true, false, false}); - auto mat = reducedBellMatrix.getMatrix(2); + auto mat = dd::getMatrix(reducedBellMatrix, 2); auto const zero = CVec{{0., 0.}, {0., 0.}, {0., 0.}, {0., 0.}}; EXPECT_EQ(mat[2], zero); EXPECT_EQ(mat[3], zero); @@ -840,7 +840,7 @@ TEST(DDPackageTest, GarbageMatrix) { dd->incRef(bellMatrix); reducedBellMatrix = dd->reduceGarbage(bellMatrix, {true, false, false, false}); - mat = reducedBellMatrix.getMatrix(2); + mat = dd::getMatrix(reducedBellMatrix, 2); EXPECT_EQ(mat[1], zero); EXPECT_EQ(mat[3], zero); @@ -858,13 +858,13 @@ TEST(DDPackageTest, ReduceGarbageVector) { auto const zeroState = makeZeroState(3, *dd); auto initialState = dd->multiply(dd->multiply(hGate, xGate), zeroState); std::cout << "Initial State:\n"; - initialState.printVector(); + dd::printVector(initialState); dd->incRef(initialState); auto const reducedState = dd->reduceGarbage(initialState, {false, true, true}); std::cout << "After reduceGarbage():\n"; - reducedState.printVector(); + dd::printVector(reducedState); EXPECT_EQ(reducedState, makeZeroState(3, *dd)); dd->incRef(initialState); @@ -885,13 +885,13 @@ TEST(DDPackageTest, ReduceGarbageVectorTGate) { auto initialState = dd->multiply( dd->multiply(tdgGate0, dd->multiply(xGate0, xGate1)), zeroState); std::cout << "Initial State:\n"; - initialState.printVector(); + dd::printVector(initialState); dd->incRef(initialState); auto const reducedState = dd->reduceGarbage(initialState, {false, false}, true); std::cout << "After reduceGarbage():\n"; - reducedState.printVector(); + dd::printVector(reducedState); EXPECT_EQ(reducedState, dd->multiply(dd->multiply(xGate0, xGate1), zeroState)); } @@ -904,13 +904,13 @@ TEST(DDPackageTest, ReduceGarbageMatrix) { auto const initialState = dd->multiply(hGate, cNotGate); std::cout << "Initial State:\n"; - initialState.printMatrix(dd->qubits()); + dd::printMatrix(initialState, dd->qubits()); dd->incRef(initialState); auto const reducedState1 = dd->reduceGarbage(initialState, {false, true, true}, true, true); std::cout << "After reduceGarbage(q1 and q2 are garbage):\n"; - reducedState1.printMatrix(dd->qubits()); + dd::printMatrix(reducedState1, dd->qubits()); auto const expectedMatrix1 = CMat{ {SQRT2_2, SQRT2_2, SQRT2_2, SQRT2_2, SQRT2_2, SQRT2_2, SQRT2_2, SQRT2_2}, @@ -922,13 +922,13 @@ TEST(DDPackageTest, ReduceGarbageMatrix) { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, }; - EXPECT_EQ(reducedState1.getMatrix(dd->qubits()), expectedMatrix1); + EXPECT_EQ(dd::getMatrix(reducedState1, dd->qubits()), expectedMatrix1); dd->incRef(initialState); auto const reducedState2 = dd->reduceGarbage(initialState, {true, false, false}, true, true); std::cout << "After reduceGarbage(q0 is garbage):\n"; - reducedState2.printMatrix(dd->qubits()); + dd::printMatrix(reducedState2, dd->qubits()); auto const expectedMatrix2 = CMat{ {1, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, @@ -936,7 +936,7 @@ TEST(DDPackageTest, ReduceGarbageMatrix) { {0, 0, 0, 0, 1, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, }; - EXPECT_EQ(reducedState2.getMatrix(dd->qubits()), expectedMatrix2); + EXPECT_EQ(dd::getMatrix(reducedState2, dd->qubits()), expectedMatrix2); } TEST(DDPackageTest, ReduceGarbageMatrix2) { @@ -955,18 +955,18 @@ TEST(DDPackageTest, ReduceGarbageMatrix2) { auto const c2 = dd->multiply(controlledHGate, xGate); std::cout << "c1:\n"; - c1.printMatrix(dd->qubits()); + dd::printMatrix(c1, dd->qubits()); std::cout << "reduceGarbage:\n"; dd->incRef(c1); auto const c1Reduced = dd->reduceGarbage(c1, {false, true, true}, true, true); - c1Reduced.printMatrix(dd->qubits()); + dd::printMatrix(c1Reduced, dd->qubits()); std::cout << "c2:\n"; - c2.printMatrix(dd->qubits()); + dd::printMatrix(c2, dd->qubits()); std::cout << "reduceGarbage:\n"; dd->incRef(c2); auto const c2Reduced = dd->reduceGarbage(c2, {false, true, true}, true, true); - c2Reduced.printMatrix(dd->qubits()); + dd::printMatrix(c2Reduced, dd->qubits()); EXPECT_EQ(c1Reduced, c2Reduced); } @@ -981,11 +981,11 @@ TEST(DDPackageTest, ReduceGarbageMatrixNoGarbage) { auto const c2 = dd->multiply(tdgGate0, tdgGate1); std::cout << "c2:\n"; - c2.printMatrix(dd->qubits()); + dd::printMatrix(c2, dd->qubits()); std::cout << "reduceGarbage:\n"; dd->incRef(c2); auto const c2Reduced = dd->reduceGarbage(c2, {false, false}, true, true); - c2Reduced.printMatrix(dd->qubits()); + dd::printMatrix(c2Reduced, dd->qubits()); EXPECT_EQ(c1, c2Reduced); } @@ -1000,18 +1000,18 @@ TEST(DDPackageTest, ReduceGarbageMatrixTGate) { auto const c2 = dd->multiply(tdgGate0, tdgGate1); std::cout << "c1:\n"; - c1.printMatrix(dd->qubits()); + dd::printMatrix(c1, dd->qubits()); std::cout << "reduceGarbage:\n"; dd->incRef(c1); auto const c1Reduced = dd->reduceGarbage(c1, {false, true}, true, true); - c1Reduced.printMatrix(dd->qubits()); + dd::printMatrix(c1Reduced, dd->qubits()); std::cout << "c2:\n"; - c2.printMatrix(dd->qubits()); + dd::printMatrix(c2, dd->qubits()); std::cout << "reduceGarbage:\n"; dd->incRef(c2); auto const c2Reduced = dd->reduceGarbage(c2, {false, true}, true, true); - c2Reduced.printMatrix(dd->qubits()); + dd::printMatrix(c2Reduced, dd->qubits()); EXPECT_EQ(c1Reduced, c2Reduced); } @@ -1235,7 +1235,7 @@ TEST(DDPackageTest, UniqueTableGrowthPreservesLookupsStatisticsAndRoots) { package.resize(0); package.resize(1); const auto fresh = makeZeroState(1, package); - EXPECT_EQ(fresh.getVector(), (CVec{1., 0.})); + EXPECT_EQ(dd::getVector(fresh), (CVec{1., 0.})); package.decRef(fresh); } @@ -1322,8 +1322,8 @@ TEST(DDPackageTest, SpecialCaseTerminal) { EXPECT_TRUE(dd->kronecker(one, one, 0).isOneTerminal()); EXPECT_EQ(one.getValueByPath(0, ""), 1.); - EXPECT_EQ(one.getValueByIndex(0), 1.); - EXPECT_EQ(mEdge::one().getValueByIndex(0, 0, 0), 1.); + EXPECT_EQ(dd::getValueByIndex(one, 0), 1.); + EXPECT_EQ(dd::getValueByIndex(mEdge::one(), 0, 0, 0), 1.); EXPECT_EQ(dd->innerProduct(zero, zero), ComplexValue(0.)); } @@ -1363,7 +1363,7 @@ TEST(DDPackageTest, KroneckerIdentityHandling) { auto const id = Package::makeIdent(); // kronecker both DDs const auto combined = dd->kronecker(h, id, 1); - const auto matrix = combined.getMatrix(dd->qubits()); + const auto matrix = dd::getMatrix(combined, dd->qubits()); const auto expectedMatrix = CMat{ {SQRT2_2, 0, 0, 0, SQRT2_2, 0, 0, 0}, {0, SQRT2_2, 0, 0, 0, SQRT2_2, 0, 0}, @@ -1380,14 +1380,14 @@ TEST(DDPackageTest, KroneckerIdentityHandling) { TEST(DDPackageTest, KroneckerRespectsBottomWidthAcrossCalls) { Package package(4); const auto top = package.makeGateDD(H_MAT, 0); - const auto topDense = top.getMatrix(1); + const auto topDense = dd::getMatrix(top, 1); for (const auto& bottom : {Package::makeIdent(), package.makeGateDD(X_MAT, 0)}) { for (const size_t width : {1U, 2U, 3U, 2U, 1U}) { SCOPED_TRACE(width); - const auto bottomDense = bottom.getMatrix(width); + const auto bottomDense = dd::getMatrix(bottom, width); const auto result = package.kronecker(top, bottom, width); - const auto actual = result.getMatrix(width + 1); + const auto actual = dd::getMatrix(result, width + 1); const auto dim = bottomDense.size(); for (size_t row = 0; row < actual.size(); ++row) { for (size_t col = 0; col < actual.size(); ++col) { @@ -1407,7 +1407,7 @@ TEST(DDPackageTest, KroneckerRespectsIndexModeAcrossCalls) { const auto result = package.kronecker(top, bottom, 1, shift); const auto expected = package.multiply(package.makeGateDD(H_MAT, shift ? 3 : 2), bottom); - EXPECT_EQ(result.getMatrix(4), expected.getMatrix(4)); + EXPECT_EQ(dd::getMatrix(result, 4), dd::getMatrix(expected, 4)); } } @@ -1428,7 +1428,7 @@ TEST(DDPackageTest, EmbedsDenseMatricesInOperandOrder) { continue; } const auto actual = - package.makeGateDD(entries, targets, controls).getMatrix(6); + dd::getMatrix(package.makeGateDD(entries, targets, controls), 6); const auto localIndex = [targets](const size_t index) { size_t result = 0; for (const auto target : targets) { @@ -1718,7 +1718,7 @@ TEST(DDPackageTest, NearZeroNormalize) { edge.p->e = {vEdge::one(), vEdge::one()}; } auto const veNormalizedCached = - vCachedEdge::normalize(ve.p, edges, dd->vMemoryManager, dd->cn); + dd::normalize(ve.p, edges, dd->vMemoryManager, dd->cn); EXPECT_EQ(veNormalizedCached, vCachedEdge::zero()); std::array edges2{}; @@ -1729,7 +1729,7 @@ TEST(DDPackageTest, NearZeroNormalize) { edge.p->e = {vEdge::one(), vEdge::one()}; } auto const veNormalized = - vEdge::normalize(ve.p, edges2, dd->vMemoryManager, dd->cn); + dd::normalize(ve.p, edges2, dd->vMemoryManager, dd->cn); EXPECT_TRUE(veNormalized.isZeroTerminal()); mEdge me{}; @@ -1744,7 +1744,7 @@ TEST(DDPackageTest, NearZeroNormalize) { edge.p->e = {mEdge::one(), mEdge::one(), mEdge::one(), mEdge::one()}; } auto const meNormalizedCached = - mCachedEdge::normalize(me.p, edges3, dd->mMemoryManager, dd->cn); + dd::normalize(me.p, edges3, dd->mMemoryManager, dd->cn); EXPECT_EQ(meNormalizedCached, mCachedEdge::zero()); me.p = dd->mMemoryManager.get(); @@ -1756,7 +1756,7 @@ TEST(DDPackageTest, NearZeroNormalize) { edge.p->e = {mEdge::one(), mEdge::one(), mEdge::one(), mEdge::one()}; } auto const meNormalized = - mEdge::normalize(me.p, edges4, dd->mMemoryManager, dd->cn); + dd::normalize(me.p, edges4, dd->mMemoryManager, dd->cn); EXPECT_TRUE(meNormalized.isZeroTerminal()); } @@ -1771,7 +1771,7 @@ TEST(DDPackageTest, DestructiveMeasurementAll) { std::mt19937_64 mt{0}; // NOLINT(ms - const CVec vBefore = plusState.getVector(); + const CVec vBefore = dd::getVector(plusState); ASSERT_EQ(vBefore[0], vBefore[1]); ASSERT_EQ(vBefore[0], vBefore[2]); @@ -1779,7 +1779,7 @@ TEST(DDPackageTest, DestructiveMeasurementAll) { const std::string m = dd->measureAll(plusState, true, mt); - const CVec vAfter = plusState.getVector(); + const CVec vAfter = dd::getVector(plusState); const int i = std::stoi(m, nullptr, 2); ASSERT_EQ(vAfter[static_cast(i)], 1.); @@ -1819,7 +1819,7 @@ TEST(DDPackageTest, DestructiveMeasurementOne) { std::mt19937_64 mt{0}; // NOLINT(cert-msc51-cpp) const char m = dd->measureOneCollapsing(plusState, 0, mt); - const CVec vAfter = plusState.getVector(); + const CVec vAfter = dd::getVector(plusState); ASSERT_EQ(m, '0'); ASSERT_EQ(vAfter[0], SQRT2_2); @@ -1879,7 +1879,7 @@ TEST(DDPackageTest, CollapsingMeasurementPreservesComplexAmplitudes) { package.incRef(state); package.performCollapsingMeasurement(state, qubit, probability, measureZero); - const auto actual = state.getVector(); + const auto actual = dd::getVector(state); for (size_t i = 0; i < amplitudes.size(); ++i) { const auto expected = ((i >> qubit) & 1U) == (measureZero ? 0U : 1U) ? amplitudes[i] / std::sqrt(probability) @@ -1905,7 +1905,7 @@ TEST(DDPackageTest, FullMeasurementPreservesBitOrder) { std::mt19937_64 actualRng(17); for (const bool collapse : {false, true}) { EXPECT_EQ(package.measureAll(state, collapse, actualRng), expected); - EXPECT_EQ(state.getValueByIndex((1U << 16U) | (1U << 5U) | 1U), 1.); + EXPECT_EQ(dd::getValueByIndex(state, (1U << 16U) | (1U << 5U) | 1U), 1.); } package.decRef(state); } @@ -2071,7 +2071,7 @@ TEST(DDPackageTest, NormalizationNumericStabilityTest) { auto const p = dd->makeGateDD(phaseMatrix(lambda), 0); auto const pdag = dd->makeGateDD(phaseMatrix(-lambda), 0); auto const result = dd->multiply(p, pdag); - EXPECT_TRUE(result.isIdentity()); + EXPECT_TRUE(dd::isIdentity(result)); dd->cUniqueTable.clear(); dd->cMemoryManager.reset(); } @@ -2327,7 +2327,7 @@ TEST(DDPackageTest, DDFromSingleQubitMatrix) { constexpr auto nrQubits = 1U; const auto dd = std::make_unique(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); - const auto outputMatrix = matDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(matDD, dd->qubits()); EXPECT_EQ(inputMatrix, outputMatrix); } @@ -2339,7 +2339,7 @@ TEST(DDPackageTest, DDFromTwoQubitMatrix) { constexpr auto nrQubits = 2U; const auto dd = std::make_unique(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); - const auto outputMatrix = matDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(matDD, dd->qubits()); EXPECT_EQ(inputMatrix, outputMatrix); } @@ -2355,7 +2355,7 @@ TEST(DDPackageTest, DDFromTwoQubitAsymmetricalMatrix) { constexpr auto nrQubits = 2U; const auto dd = std::make_unique(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); - const auto outputMatrix = matDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(matDD, dd->qubits()); EXPECT_EQ(inputMatrix, outputMatrix); } @@ -2372,7 +2372,7 @@ TEST(DDPackageTest, DDFromThreeQubitMatrix) { const auto dd = std::make_unique(nrQubits); const auto matDD = dd->makeDDFromMatrix(inputMatrix); - const auto outputMatrix = matDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(matDD, dd->qubits()); EXPECT_EQ(inputMatrix, outputMatrix); } @@ -2583,14 +2583,15 @@ TEST(DDPackageTest, ArithmeticAcrossSkippedMatrixLevels) { } for (const auto& left : operands) { for (const auto& right : operands) { - const auto a = left.getMatrix(qubits); - const auto b = right.getMatrix(qubits); + const auto a = dd::getMatrix(left, qubits); + const auto b = dd::getMatrix(right, qubits); for (const bool collect : {false, true}) { if (collect) { package.garbageCollect(true); } - const auto sum = package.add(left, right).getMatrix(qubits); - const auto product = package.multiply(left, right).getMatrix(qubits); + const auto sum = dd::getMatrix(package.add(left, right), qubits); + const auto product = + dd::getMatrix(package.multiply(left, right), qubits); for (size_t row = 0; row < dimension; ++row) { for (size_t col = 0; col < dimension; ++col) { std::complex expected{}; @@ -2606,8 +2607,8 @@ TEST(DDPackageTest, ArithmeticAcrossSkippedMatrixLevels) { } } /// A scalar vector still needs zero extension through skipped matrix levels. - const auto vector = package.multiply(x, vEdge::one()).getVector(); - const auto matrix = x.getMatrix(qubits); + const auto vector = dd::getVector(package.multiply(x, vEdge::one())); + const auto matrix = dd::getMatrix(x, qubits); ASSERT_EQ(vector.size(), dimension); for (size_t row = 0; row < dimension; ++row) { EXPECT_NEAR(std::abs(vector[row] - matrix[row][0]), 0., 1e-12); @@ -2727,7 +2728,7 @@ TEST(DDPackageTest, ReduceAncillaRegression) { dd->incRef(inputDD); const auto outputDD = dd->reduceAncillae(inputDD, {true, false}); - const auto outputMatrix = outputDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); const auto expected = CMat{{1, 0, 1, 0}, {1, 0, 1, 0}, {1, 0, -1, 0}, {1, 0, -1, 0}}; @@ -2823,12 +2824,12 @@ TEST(DDPackageTest, WideMagnitudeAdditionRetainsNormalization) { TEST(DDPackageTest, ConjugationAfterGarbageCollection) { auto dd = std::make_unique(1); const auto input = makeStateFromVector({SQRT2_2, {0., SQRT2_2}}, *dd); - const auto expected = dd->conjugate(input).getVector(); + const auto expected = dd::getVector(dd->conjugate(input)); /// Cached results are unreferenced and may be collected and reused. ASSERT_TRUE(dd->garbageCollect(true)); const auto replacement = makeZeroState(1, *dd); - EXPECT_EQ(dd->conjugate(input).getVector(), expected); + EXPECT_EQ(dd::getVector(dd->conjugate(input)), expected); dd->decRef(input); dd->decRef(replacement); } @@ -2841,12 +2842,12 @@ TEST(DDPackageTest, VectorMagnitudeAdditionAfterGarbageCollection) { const vCachedEdge y{b.p, b.w}; const auto result = dd->addMagnitudes(x, y, 0); const auto expected = - vEdge{.p = result.p, .w = dd->cn.lookup(result.w)}.getVector(); + dd::getVector(vEdge{.p = result.p, .w = dd->cn.lookup(result.w)}); ASSERT_TRUE(dd->garbageCollect(true)); const auto replacement = makeStateFromVector({0., 1.}, *dd); const auto again = dd->addMagnitudes(x, y, 0); - EXPECT_EQ((vEdge{.p = again.p, .w = dd->cn.lookup(again.w)}.getVector()), + EXPECT_EQ((dd::getVector(vEdge{.p = again.p, .w = dd->cn.lookup(again.w)})), expected); dd->decRef(a); dd->decRef(b); @@ -2863,14 +2864,15 @@ TEST(DDPackageTest, MatrixMagnitudeAdditionAfterGarbageCollection) { const mCachedEdge y{b.p, b.w}; const auto result = dd->addMagnitudes(x, y, 0); const auto expected = - mEdge{.p = result.p, .w = dd->cn.lookup(result.w)}.getMatrix(1); + dd::getMatrix(mEdge{.p = result.p, .w = dd->cn.lookup(result.w)}, 1); ASSERT_TRUE(dd->garbageCollect(true)); const auto replacement = getDD(TestGate(0, Fixture::X), *dd); dd->incRef(replacement); const auto again = dd->addMagnitudes(x, y, 0); - EXPECT_EQ((mEdge{.p = again.p, .w = dd->cn.lookup(again.w)}.getMatrix(1)), - expected); + EXPECT_EQ( + (dd::getMatrix(mEdge{.p = again.p, .w = dd->cn.lookup(again.w)}, 1)), + expected); dd->decRef(a); dd->decRef(b); dd->decRef(replacement); @@ -2894,12 +2896,12 @@ TEST(DDPackageTest, VectorConjugate) { const auto vecDD = makeStateFromVector(vec, *dd); std::cout << "Vector:\n"; - vecDD.printVector(); + dd::printVector(vecDD); const auto conjVecDD = dd->conjugate(vecDD); std::cout << "Conjugated vector:\n"; - conjVecDD.printVector(); + dd::printVector(conjVecDD); - const auto conjVec = conjVecDD.getVector(); + const auto conjVec = dd::getVector(conjVecDD); for (auto i = 0U; i < vec.size(); ++i) { constexpr auto tolerance = 1e-10; EXPECT_NEAR(conjVec[i].real(), vec[i].real(), tolerance); @@ -2912,7 +2914,7 @@ TEST(DDPackageTest, ReduceAncillaIdentity) { auto const inputDD = Package::makeIdent(); const auto outputDD = dd->reduceAncillae(inputDD, {true, true}); - const auto outputMatrix = outputDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); const auto expected = CMat{{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; @@ -2926,7 +2928,7 @@ TEST(DDPackageTest, ReduceAncillaIdentityBeforeFirstNode) { dd->incRef(xGate); const auto outputDD = dd->reduceAncillae(xGate, {false, true}); - const auto outputMatrix = outputDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); const auto expected = CMat{{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; @@ -2939,7 +2941,7 @@ TEST(DDPackageTest, ReduceAncillaIdentityAfterLastNode) { dd->incRef(xGate); const auto outputDD = dd->reduceAncillae(xGate, {true, false}); - const auto outputMatrix = outputDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); const auto expected = CMat{{0, 0, 1, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}; @@ -2954,7 +2956,7 @@ TEST(DDPackageTest, ReduceAncillaIdentityBetweenTwoNodes) { dd->incRef(state); const auto outputDD = dd->reduceAncillae(state, {false, true, false}); - const auto outputMatrix = outputDD.getMatrix(dd->qubits()); + const auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); const auto expected = CMat{ {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, @@ -2969,14 +2971,14 @@ TEST(DDPackageTest, ReduceGarbageIdentity) { auto const inputDD = Package::makeIdent(); auto outputDD = dd->reduceGarbage(inputDD, {true, true}); - auto outputMatrix = outputDD.getMatrix(dd->qubits()); + auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); auto expected = CMat{{1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; EXPECT_EQ(outputMatrix, expected); // test also for non-regular garbage reduction as well outputDD = dd->reduceGarbage(inputDD, {true, true}, false); - outputMatrix = outputDD.getMatrix(dd->qubits()); + outputMatrix = dd::getMatrix(outputDD, dd->qubits()); expected = CMat{{1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}}; EXPECT_EQ(outputMatrix, expected); } @@ -2988,7 +2990,7 @@ TEST(DDPackageTest, ReduceGarbageIdentityBeforeFirstNode) { auto outputDD = dd->reduceGarbage(xGate, {false, true}); - auto outputMatrix = outputDD.getMatrix(dd->qubits()); + auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); auto expected = CMat{{0, 1, 0, 1}, {1, 0, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; EXPECT_EQ(outputMatrix, expected); @@ -2996,7 +2998,7 @@ TEST(DDPackageTest, ReduceGarbageIdentityBeforeFirstNode) { dd->incRef(xGate); outputDD = dd->reduceGarbage(xGate, {false, true}, false); - outputMatrix = outputDD.getMatrix(dd->qubits()); + outputMatrix = dd::getMatrix(outputDD, dd->qubits()); expected = CMat{{0, 1, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {1, 0, 0, 0}}; EXPECT_EQ(outputMatrix, expected); } @@ -3008,7 +3010,7 @@ TEST(DDPackageTest, ReduceGarbageIdentityAfterLastNode) { auto outputDD = dd->reduceGarbage(xGate, {true, false}); - auto outputMatrix = outputDD.getMatrix(dd->qubits()); + auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); auto expected = CMat{{0, 0, 1, 1}, {0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}; EXPECT_EQ(outputMatrix, expected); @@ -3016,7 +3018,7 @@ TEST(DDPackageTest, ReduceGarbageIdentityAfterLastNode) { dd->incRef(xGate); outputDD = dd->reduceGarbage(xGate, {true, false}, false); - outputMatrix = outputDD.getMatrix(dd->qubits()); + outputMatrix = dd::getMatrix(outputDD, dd->qubits()); expected = CMat{{0, 0, 1, 0}, {0, 0, 1, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}}; EXPECT_EQ(outputMatrix, expected); } @@ -3029,7 +3031,7 @@ TEST(DDPackageTest, ReduceGarbageIdentityBetweenTwoNodes) { dd->incRef(state); auto outputDD = dd->reduceGarbage(state, {false, true, false}); - auto outputMatrix = outputDD.getMatrix(dd->qubits()); + auto outputMatrix = dd::getMatrix(outputDD, dd->qubits()); auto expected = CMat{ {0, 0, 0, 0, 0, 1, 0, 1}, {0, 0, 0, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, @@ -3042,7 +3044,7 @@ TEST(DDPackageTest, ReduceGarbageIdentityBetweenTwoNodes) { dd->incRef(state); outputDD = dd->reduceGarbage(state, {false, true, false}, false); - outputMatrix = outputDD.getMatrix(dd->qubits()); + outputMatrix = dd::getMatrix(outputDD, dd->qubits()); expected = CMat{ {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, @@ -3092,7 +3094,7 @@ TEST(DDPackageTest, WideHadamardMatricesPreserveRootScale) { for (size_t repetition = 0; repetition < 2; ++repetition) { check(package.kronecker(matrix, h, 1), width + 1, expected * SQRT2_2); check(package.kronecker(h, matrix, width), width + 1, expected * SQRT2_2); - EXPECT_TRUE(package.multiply(matrix, matrix).isIdentity(false)); + EXPECT_TRUE(dd::isIdentity(package.multiply(matrix, matrix), false)); } package.garbageCollect(true); check(matrix, width, expected); @@ -3100,7 +3102,7 @@ TEST(DDPackageTest, WideHadamardMatricesPreserveRootScale) { matrix = package.applyOperation( package.makeGateDD(H_MAT, static_cast(q)), matrix); } - EXPECT_TRUE(matrix.isIdentity(false)); + EXPECT_TRUE(dd::isIdentity(matrix, false)); package.decRef(matrix); } @@ -3183,12 +3185,12 @@ TEST(DDPackageTest, KroneckerRestoresNormalizationAfterInterningChanges) { 0, std::array{vCachedEdge::one(), vCachedEdge::zero()}); const vEdge x{.p = raw.p, .w = package.cn.lookup(10.)}; const vEdge y{.p = zero.p, .w = package.cn.lookup(10.)}; - const auto expected = x.getValueByIndex(0) * y.getValueByIndex(0); + const auto expected = dd::getValueByIndex(x, 0) * dd::getValueByIndex(y, 0); const auto nearer = package.cn.lookup(dominant - (.5 * RealNumber::eps)); ASSERT_NE(nearer.r, initial.r); for (size_t repetition = 0; repetition < 2; ++repetition) { const auto result = package.kronecker(x, y, 1); - EXPECT_NEAR(std::abs(result.getValueByIndex(0) - expected), 0., 1e-13); + EXPECT_NEAR(std::abs(dd::getValueByIndex(result, 0) - expected), 0., 1e-13); } } diff --git a/test/dd/test_state_generation.cpp b/test/dd/test_state_generation.cpp index 6c4898930a..859c62cdd2 100644 --- a/test/dd/test_state_generation.cpp +++ b/test/dd/test_state_generation.cpp @@ -9,6 +9,7 @@ */ #include "dd/DDDefinitions.hpp" +#include "dd/Edge.hpp" #include "dd/Node.hpp" #include "dd/Package.hpp" #include "dd/StateGeneration.hpp" @@ -53,7 +54,7 @@ TEST(StateGenerationTest, MakeZero) { auto dd = std::make_unique(nq); auto const zero = makeZeroState(nq, *dd); - EXPECT_EQ(zero.getVector(), vec); + EXPECT_EQ(dd::getVector(zero), vec); dd->decRef(zero); dd->garbageCollect(true); @@ -78,7 +79,7 @@ TEST(StateGenerationTest, MakeBasis) { auto dd = std::make_unique(nq); auto const basis = makeBasisState(nq, state, *dd); - EXPECT_EQ(basis.getVector(), vec); + EXPECT_EQ(dd::getVector(basis), vec); dd->decRef(basis); dd->garbageCollect(true); @@ -110,7 +111,7 @@ TEST(StateGenerationTest, MakeBasisDifficult) { auto dd = std::make_unique(nq); auto const basis = makeBasisState(nq, state, *dd); - expectStateVectorNear(basis.getVector(), vec); + expectStateVectorNear(dd::getVector(basis), vec); dd->decRef(basis); dd->garbageCollect(true); @@ -134,7 +135,7 @@ TEST(StateGenerationTest, MakeGHZ) { auto dd = std::make_unique(nq); auto const ghz = makeGHZState(nq, *dd); - expectStateVectorNear(ghz.getVector(), vec); + expectStateVectorNear(dd::getVector(ghz), vec); dd->decRef(ghz); dd->garbageCollect(true); @@ -177,7 +178,7 @@ TEST(StateGenerationTest, MakeW) { auto dd = std::make_unique(nq); auto const w = makeWState(nq, *dd); - expectStateVectorNear(w.getVector(), vec); + expectStateVectorNear(dd::getVector(w), vec); dd->decRef(w); dd->garbageCollect(true); @@ -340,7 +341,7 @@ TEST(StateGenerationTest, VectorConstructionChecksCapacity) { EXPECT_FALSE(read); const auto state = makeStateFromVector(CVec{{0.5, 0.25}, {-0.5, 0.75}}, oneQubit); - expectStateVectorNear(state.getVector(), {{0.5, 0.25}, {-0.5, 0.75}}); + expectStateVectorNear(dd::getVector(state), {{0.5, 0.25}, {-0.5, 0.75}}); EXPECT_NO_THROW(oneQubit.decRef(state)); } @@ -375,8 +376,8 @@ TEST(StateGenerationTest, BasisConstructionUsesRequestedPrefix) { const auto product = makeBasisState(width, basis, package); const auto zero = makeZeroState(width, package); EXPECT_EQ(binary, product); - EXPECT_EQ(binary.getValueByIndex(13U & ((1U << width) - 1U)), 1.); - EXPECT_EQ(zero.getValueByIndex(0), 1.); + EXPECT_EQ(dd::getValueByIndex(binary, 13U & ((1U << width) - 1U)), 1.); + EXPECT_EQ(dd::getValueByIndex(zero, 0), 1.); package.decRef(binary); package.decRef(product); package.decRef(zero); diff --git a/test/dd/test_tables.cpp b/test/dd/test_tables.cpp index 55008611a7..5cc5199882 100644 --- a/test/dd/test_tables.cpp +++ b/test/dd/test_tables.cpp @@ -12,6 +12,7 @@ #include "dd/ComputeTable.hpp" #include "dd/DDDefinitions.hpp" #include "dd/DDpackageConfig.hpp" +#include "dd/Edge.hpp" #include "dd/MemoryManager.hpp" #include "dd/Node.hpp" #include "dd/Package.hpp" @@ -127,7 +128,7 @@ TEST(DDTableTest, RehashPreservesCanonicalNodesAndOwnedRoots) { }, 0), gates[i - 1]); - const auto values = states[i - 1].getVector(); + const auto values = dd::getVector(states[i - 1]); ASSERT_EQ(values.size(), 2); EXPECT_NEAR(values[0].real(), std::cos(angle), 1e-12); EXPECT_NEAR(values[1].real(), std::sin(angle), 1e-12); @@ -145,7 +146,7 @@ TEST(DDTableTest, RehashPreservesCanonicalNodesAndOwnedRoots) { EXPECT_EQ(table->getStats(0).numEntries, 0); } const auto fresh = makeZeroState(2, package); - EXPECT_EQ(fresh.getVector(), (CVec{1., 0., 0., 0.})); + EXPECT_EQ(dd::getVector(fresh), (CVec{1., 0., 0., 0.})); package.decRef(fresh); } @@ -242,13 +243,14 @@ TEST(DDTableTest, AdaptiveMultiplicationCachePreservesStateAcrossCollection) { EXPECT_EQ(stats.numBuckets, initialBuckets); for (size_t i = 0; i < 2 * initialBuckets; ++i) { const auto result = package.multiply(gate, state); - EXPECT_NEAR(result.getValueByIndex(0).real(), expected[0].real(), 1e-12); + EXPECT_NEAR(dd::getValueByIndex(result, 0).real(), expected[0].real(), + 1e-12); } ASSERT_GE(stats.hits, initialBuckets); ASSERT_TRUE(package.garbageCollect(true)); EXPECT_EQ(stats.numBuckets, initialBuckets == 4 ? 32 : initialBuckets); EXPECT_EQ(stats.numEntries, 0); - const auto result = package.multiply(gate, state).getVector(); + const auto result = dd::getVector(package.multiply(gate, state)); for (size_t i = 0; i < result.size(); ++i) { EXPECT_NEAR(result[i].real(), expected[i].real(), 1e-12); EXPECT_NEAR(result[i].imag(), expected[i].imag(), 1e-12);