diff --git a/.clang-format b/.clang-format index 11d0a4ab06..c862c60400 100644 --- a/.clang-format +++ b/.clang-format @@ -5,8 +5,6 @@ IncludeCategories: Priority: 1 - Regex: '^<.*\.(h|hpp)>' Priority: 2 - - Regex: "^" - Priority: 2 - Regex: "^<.*>" Priority: 3 PointerAlignment: Left diff --git a/CHANGELOG.md b/CHANGELOG.md index c8b192e5bf..b24ffa3f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637], [#1676], [#1706]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects - ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1717], [#1728], [#1730], [#1749], [#1762], [#1765]) + ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1717], [#1728], [#1730], [#1749], [#1762], [#1765], [#1774]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -402,6 +402,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1774]: https://github.com/munich-quantum-toolkit/core/pull/1774 [#1765]: https://github.com/munich-quantum-toolkit/core/pull/1765 [#1762]: https://github.com/munich-quantum-toolkit/core/pull/1762 [#1749]: https://github.com/munich-quantum-toolkit/core/pull/1749 diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index 704a382ce9..e60652a668 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -23,17 +23,6 @@ if(BUILD_MQT_CORE_BINDINGS) endif() if(BUILD_MQT_CORE_MLIR) - set(Eigen_VERSION - 5.0.1 - CACHE STRING "Eigen version") - set(Eigen_URL - https://gitlab.com/libeigen/eigen/-/archive/${Eigen_VERSION}/eigen-${Eigen_VERSION}.tar.gz) - set(EIGEN_BUILD_TESTING - OFF - CACHE INTERNAL "Disable building Eigen tests") - FetchContent_Declare(Eigen URL ${Eigen_URL} FIND_PACKAGE_ARGS ${Eigen_VERSION}) - list(APPEND FETCH_PACKAGES Eigen) - # Fetch jeff-mlir FetchContent_Declare( jeff-mlir @@ -132,25 +121,6 @@ list(APPEND FETCH_PACKAGES spdlog) # Make all declared dependencies available. FetchContent_MakeAvailable(${FETCH_PACKAGES}) -# Treat Eigen headers as system headers to avoid surfacing third-party warnings. -set(_eigen_target "") -if(TARGET Eigen3::Eigen) - set(_eigen_target Eigen3::Eigen) -elseif(TARGET Eigen::Eigen) - set(_eigen_target Eigen::Eigen) -endif() -if(_eigen_target) - get_target_property(_eigen_alias_target ${_eigen_target} ALIASED_TARGET) - if(_eigen_alias_target) - set(_eigen_target ${_eigen_alias_target}) - endif() - get_target_property(_eigen_includes ${_eigen_target} INTERFACE_INCLUDE_DIRECTORIES) - if(_eigen_includes) - set_target_properties(${_eigen_target} PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES - "${_eigen_includes}") - endif() -endif() - # Install nlohmann_json with explicit MQT components. if(MQT_CORE_JSON_INSTALL AND TARGET nlohmann_json) set(MQT_CORE_JSON_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/cmake/nlohmann_json") diff --git a/mlir/.clang-tidy b/mlir/.clang-tidy index a83fa04cb3..3fb5c4cc0e 100644 --- a/mlir/.clang-tidy +++ b/mlir/.clang-tidy @@ -11,7 +11,3 @@ Checks: | -misc-use-anonymous-namespace, -modernize-type-traits, -*-const-correctness, - -CheckOptions: - - key: misc-include-cleaner.IgnoreHeaders - value: "Eigen/.*;unsupported/Eigen/.*" diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h index 035b0b5268..0940cea2af 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h @@ -10,7 +10,8 @@ #pragma once -#include +#include "mlir/Dialect/QCO/Utils/Matrix.h" + #include #include #include diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index 3de8255b8e..3384d7a048 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -28,57 +28,90 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { let cppNamespace = "::mlir::qco"; - // Generic implementation body for getUnitaryMatrix methods - defvar unitaryMatrixMethodBody = [{ - auto process = [&](MatrixType&& m) -> bool { - using TargetT = std::remove_cvref_t; - using SourceT = std::remove_cvref_t; - - constexpr bool isTargetDynamic = - (TargetT::SizeAtCompileTime == Eigen::Dynamic); - constexpr bool isSourceDynamic = - (SourceT::SizeAtCompileTime == Eigen::Dynamic); - - // Case 1: Target is Dynamic. Always accepts source. - if constexpr (isTargetDynamic) { - out = std::forward(m); + // Size-specific bodies + // Each helper only accepts the matching matrix type. + defvar unitaryMatrix1x1MethodBody = [{ + if constexpr (requires { $_op.getUnitaryMatrix().has_value(); }) { + using ValueT = std::remove_cvref_t())>; + if constexpr (std::is_same_v) { + if (auto matrix = $_op.getUnitaryMatrix()) { + out = *matrix; return true; } - // Case 2: Target is Fixed. - else { - // Case 2a: Source is Dynamic. Runtime dimension check required. - if constexpr (isSourceDynamic) { - if (m.rows() == static_cast(TargetT::RowsAtCompileTime) && - m.cols() == static_cast(TargetT::ColsAtCompileTime)) - [[likely]] { - out = std::forward(m); - return true; - } - } - // Case 2b: Source is Fixed. Compile-time check. - else if constexpr (static_cast( - SourceT::RowsAtCompileTime) == - static_cast( - TargetT::RowsAtCompileTime) && - static_cast( - SourceT::ColsAtCompileTime) == - static_cast( - TargetT::ColsAtCompileTime)) { - out = std::forward(m); - return true; - } - } return false; - }; + } else { + return false; + } + } else if constexpr (requires { $_op.getUnitaryMatrix(); }) { + using ValueT = std::remove_cvref_t; + if constexpr (std::is_same_v) { + out = $_op.getUnitaryMatrix(); + return true; + } + return false; + } else { + llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!"); + } + }]; + defvar unitaryMatrix2x2MethodBody = [{ + if constexpr (requires { $_op.getUnitaryMatrix().has_value(); }) { + using ValueT = std::remove_cvref_t())>; + if constexpr (std::is_same_v) { + if (auto matrix = $_op.getUnitaryMatrix()) { + out = *matrix; + return true; + } + return false; + } else { + return false; + } + } else if constexpr (requires { $_op.getUnitaryMatrix(); }) { + using ValueT = std::remove_cvref_t; + if constexpr (std::is_same_v) { + out = $_op.getUnitaryMatrix(); + return true; + } + return false; + } else { + llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!"); + } + }]; + defvar unitaryMatrix4x4MethodBody = [{ if constexpr (requires { $_op.getUnitaryMatrix().has_value(); }) { - if (auto&& matrix = $_op.getUnitaryMatrix()) { - return process(std::move(*matrix)); + using ValueT = std::remove_cvref_t())>; + if constexpr (std::is_same_v) { + if (auto matrix = $_op.getUnitaryMatrix()) { + out = *matrix; + return true; + } + return false; + } else { + return false; + } + } else if constexpr (requires { $_op.getUnitaryMatrix(); }) { + using ValueT = std::remove_cvref_t; + if constexpr (std::is_same_v) { + out = $_op.getUnitaryMatrix(); + return true; + } + return false; + } else { + llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!"); + } + }]; + + defvar unitaryMatrixDynamicMethodBody = [{ + if constexpr (requires { $_op.getUnitaryMatrix().has_value(); }) { + if (auto matrix = $_op.getUnitaryMatrix()) { + out.assignFrom(*matrix); + return true; } return false; } else if constexpr (requires { $_op.getUnitaryMatrix(); }) { - return process($_op.getUnitaryMatrix()); + out.assignFrom($_op.getUnitaryMatrix()); + return true; } else { llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!"); } @@ -156,48 +189,34 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { // Unitary matrix helpers InterfaceMethod<"Populates the given 1x1 unitary matrix if possible.", - "bool", "getUnitaryMatrix1x1", - (ins "Eigen::Matrix, 1, 1>&":$out), - unitaryMatrixMethodBody>, + "bool", "getUnitaryMatrix1x1", (ins "Matrix1x1&":$out), + unitaryMatrix1x1MethodBody>, InterfaceMethod<"Populates the given 2x2 unitary matrix if possible.", - "bool", "getUnitaryMatrix2x2", - (ins "Eigen::Matrix2cd&":$out), unitaryMatrixMethodBody>, + "bool", "getUnitaryMatrix2x2", (ins "Matrix2x2&":$out), + unitaryMatrix2x2MethodBody>, InterfaceMethod<"Populates the given 4x4 unitary matrix if possible.", - "bool", "getUnitaryMatrix4x4", - (ins "Eigen::Matrix4cd&":$out), unitaryMatrixMethodBody>, + "bool", "getUnitaryMatrix4x4", (ins "Matrix4x4&":$out), + unitaryMatrix4x4MethodBody>, InterfaceMethod<"Populates the given dynamic unitary matrix.", "bool", - "getUnitaryMatrixDynamic", (ins "Eigen::MatrixXcd&":$out), - unitaryMatrixMethodBody>]; + "getUnitaryMatrixDynamic", (ins "DynamicMatrix&":$out), + unitaryMatrixDynamicMethodBody>]; let extraClassDeclaration = [{ template std::optional getUnitaryMatrix() { + static_assert(is_supported_matrix_v, + "MatrixType must be Matrix1x1, Matrix2x2, Matrix4x4, or DynamicMatrix"); MatrixType out; bool result = false; - // Dispatch to the appropriate fixed-size or dynamic method based on the - // matrix type. - if constexpr (MatrixType::RowsAtCompileTime == 1 && - MatrixType::ColsAtCompileTime == 1) { + if constexpr (std::is_same_v) { result = this->getUnitaryMatrix1x1(out); - } else if constexpr (MatrixType::RowsAtCompileTime == 2 && - MatrixType::ColsAtCompileTime == 2) { + } else if constexpr (std::is_same_v) { result = this->getUnitaryMatrix2x2(out); - } else if constexpr (MatrixType::RowsAtCompileTime == 4 && - MatrixType::ColsAtCompileTime == 4) { + } else if constexpr (std::is_same_v) { result = this->getUnitaryMatrix4x4(out); - } else if constexpr (MatrixType::SizeAtCompileTime == Eigen::Dynamic) { + } else if constexpr (std::is_same_v) { result = this->getUnitaryMatrixDynamic(out); - } else { - // Fallback: Try obtaining dynamic matrix and see if size matches - Eigen::MatrixXcd dynamicOut; - if (this->getUnitaryMatrixDynamic(dynamicOut)) { - if (dynamicOut.rows() == MatrixType::RowsAtCompileTime && - dynamicOut.cols() == MatrixType::ColsAtCompileTime) { - out = dynamicOut; - result = true; - } - } } if (result) { diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index a5bbfb7f51..8bdaaa668a 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -200,7 +200,7 @@ def GPhaseOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "gphase"; } - [[nodiscard]] std::optional, 1, 1>> getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "const std::variant&":$theta)>]; @@ -226,7 +226,7 @@ def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "id"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -250,7 +250,7 @@ def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "x"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -274,7 +274,7 @@ def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "y"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -298,7 +298,7 @@ def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "z"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -322,7 +322,7 @@ def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "h"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -346,7 +346,7 @@ def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "s"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -371,7 +371,7 @@ def SdgOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "sdg"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -395,7 +395,7 @@ def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "t"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -420,7 +420,7 @@ def TdgOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "tdg"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -444,7 +444,7 @@ def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "sx"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -469,7 +469,7 @@ def SXdgOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "sxdg"; } - [[nodiscard]] static Eigen::Matrix2cd getUnitaryMatrix(); + [[nodiscard]] static Matrix2x2 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -494,7 +494,7 @@ def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "rx"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -523,7 +523,7 @@ def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "ry"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -552,7 +552,7 @@ def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "rz"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -581,7 +581,7 @@ def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "p"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -611,7 +611,7 @@ def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "r"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -641,7 +641,7 @@ def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "u2"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -672,7 +672,7 @@ def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "u"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit_in, @@ -705,7 +705,7 @@ def SWAPOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "swap"; } - [[nodiscard]] static Eigen::Matrix4cd getUnitaryMatrix(); + [[nodiscard]] static Matrix4x4 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -733,7 +733,7 @@ def iSWAPOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "iswap"; } - [[nodiscard]] static Eigen::Matrix4cd getUnitaryMatrix(); + [[nodiscard]] static Matrix4x4 getUnitaryMatrix(); }]; } @@ -759,7 +759,7 @@ def DCXOp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "dcx"; } - [[nodiscard]] static Eigen::Matrix4cd getUnitaryMatrix(); + [[nodiscard]] static Matrix4x4 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -787,7 +787,7 @@ def ECROp let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "ecr"; } - [[nodiscard]] static Eigen::Matrix4cd getUnitaryMatrix(); + [[nodiscard]] static Matrix4x4 getUnitaryMatrix(); }]; let hasCanonicalizer = 1; @@ -815,7 +815,7 @@ def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "rxx"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit0_in, "Value":$qubit1_in, @@ -847,7 +847,7 @@ def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "ryy"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit0_in, "Value":$qubit1_in, @@ -879,7 +879,7 @@ def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "rzx"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit0_in, "Value":$qubit1_in, @@ -911,7 +911,7 @@ def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "rzz"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit0_in, "Value":$qubit1_in, @@ -945,7 +945,7 @@ def XXPlusYYOp : QCOOp<"xx_plus_yy", let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "xx_plus_yy"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit0_in, "Value":$qubit1_in, @@ -980,7 +980,7 @@ def XXMinusYYOp : QCOOp<"xx_minus_yy", let extraClassDeclaration = [{ [[nodiscard]] static StringRef getBaseSymbol() { return "xx_minus_yy"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "Value":$qubit0_in, "Value":$qubit1_in, @@ -1060,13 +1060,12 @@ def YieldOp : QCOOp<"yield", traits = [Terminator, ReturnLike]> { let assemblyFormat = "attr-dict ($targets^ `:` type($targets))?"; } -def CtrlOp - : QCOOp<"ctrl", - traits = [UnitaryOpInterface, AttrSizedOperandSegments, - AttrSizedResultSegments, SameOperandsAndResultType, - SameOperandsAndResultShape, - SingleBlockImplicitTerminator<"::mlir::qco::YieldOp">, - RecursiveMemoryEffects]> { +def CtrlOp : QCOOp<"ctrl", + traits = [UnitaryOpInterface, AttrSizedOperandSegments, + AttrSizedResultSegments, SameOperandsAndResultType, + SameOperandsAndResultShape, + SingleBlockImplicitTerminator<"YieldOp">, + RecursiveMemoryEffects]> { let summary = "Add control qubits to a unitary operation"; let description = [{ A modifier operation that adds control qubits to the unitary operation @@ -1124,7 +1123,7 @@ def CtrlOp Value getParameter(size_t i) { return getBodyUnitary().getParameter(i); } ValueRange getParameters() { return getBodyUnitary().getParameters(); } [[nodiscard]] static StringRef getBaseSymbol() { return "ctrl"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets), @@ -1139,11 +1138,9 @@ def CtrlOp let hasVerifier = 1; } -def InvOp - : QCOOp<"inv", - traits = [UnitaryOpInterface, - SingleBlockImplicitTerminator<"::mlir::qco::YieldOp">, - RecursiveMemoryEffects]> { +def InvOp : QCOOp<"inv", traits = [UnitaryOpInterface, + SingleBlockImplicitTerminator<"YieldOp">, + RecursiveMemoryEffects]> { let summary = "Invert a unitary operation"; let description = [{ A modifier operation that inverts the unitary operation defined in its body @@ -1195,7 +1192,7 @@ def InvOp Value getParameter(size_t i) { return getBodyUnitary().getParameter(i); } ValueRange getParameters() { return getBodyUnitary().getParameters(); } [[nodiscard]] static StringRef getBaseSymbol() { return "inv"; } - [[nodiscard]] std::optional getUnitaryMatrix(); + [[nodiscard]] std::optional getUnitaryMatrix(); }]; let builders = [OpBuilder<(ins "ValueRange":$qubits), [{ @@ -1220,8 +1217,7 @@ def IfOp RegionBranchOpInterface, ["getNumRegionInvocations", "getRegionInvocationBounds", "getEntrySuccessorRegions"]>, - SingleBlock, - SingleBlockImplicitTerminator<"::mlir::qco::YieldOp">, + SingleBlock, SingleBlockImplicitTerminator<"YieldOp">, RecursiveMemoryEffects]> { let summary = "If-then-else operation for linear (qubit) types"; diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Matrix.h b/mlir/include/mlir/Dialect/QCO/Utils/Matrix.h new file mode 100644 index 0000000000..2305a06346 --- /dev/null +++ b/mlir/include/mlir/Dialect/QCO/Utils/Matrix.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mlir::qco { + +/// Complex scalar type used for matrix entries. +using Complex = std::complex; + +/// Default absolute tolerance for matrix comparisons. +inline constexpr double MATRIX_TOLERANCE = 1e-14; + +/** + * @brief 1x1 matrix for global-phase gates. + * + * Wraps a single complex scalar. Used by operations such as `GPhaseOp` whose + * unitary is a global phase factor. + */ +struct Matrix1x1 { + /// The sole matrix entry. + Complex value{0.0, 0.0}; + + /** + * @brief Constructs a matrix from its single entry. + * @param m00 Element at row 0, column 0. + * @return A new `Matrix1x1` with the given element. + */ + [[nodiscard]] static Matrix1x1 fromElements(Complex m00); + + /** + * @brief Mutable element access with `(row, col)` indexing. + * @param row Row index (must be `0`). + * @param col Column index (must be `0`). + * @return Reference to the sole matrix entry. + */ + [[nodiscard]] Complex& operator()(std::size_t row, std::size_t col); + + /** + * @brief Const element access with `(row, col)` indexing. + * @param row Row index (must be `0`). + * @param col Column index (must be `0`). + * @return Copy of the sole matrix entry. + */ + [[nodiscard]] Complex operator()(std::size_t row, std::size_t col) const; + + /** + * @brief Checks approximate equality using an absolute tolerance. + * @param other Matrix to compare against. + * @param tol Maximum allowed complex modulus of the entry difference. + * @return True if the difference is within @p tol. + */ + [[nodiscard]] bool isApprox(const Matrix1x1& other, + double tol = MATRIX_TOLERANCE) const; +}; + +/** + * @brief Fixed-size 2x2 matrix in row-major layout. + * + * Used to represent single-qubit unitaries. Elements are stored in a flat array + * with index `(row * K_COLS) + col`. + */ +struct Matrix2x2 { + /// Number of rows. + static constexpr std::size_t K_ROWS = 2; + /// Number of columns. + static constexpr std::size_t K_COLS = 2; + /// Total number of stored elements. + static constexpr std::size_t K_SIZE_AT_COMPILE_TIME = 4; + + /// Flat row-major storage of all matrix entries. + std::array data{}; + + /** + * @brief Constructs a matrix from its four row-major entries. + * @param m00 Element at row 0, column 0. + * @param m01 Element at row 0, column 1. + * @param m10 Element at row 1, column 0. + * @param m11 Element at row 1, column 1. + * @return A new `Matrix2x2` with the given elements. + */ + [[nodiscard]] static Matrix2x2 fromElements(const Complex& m00, + const Complex& m01, + const Complex& m10, + const Complex& m11); + + /** + * @brief Returns the 2x2 identity matrix. + * @return Identity matrix `[[1, 0], [0, 1]]`. + */ + [[nodiscard]] static constexpr Matrix2x2 identity() { return {{1, 0, 0, 1}}; } + + /** + * @brief Mutable element access. + * @param row Row index in `[0, K_ROWS)`. + * @param col Column index in `[0, K_COLS)`. + * @return Reference to the element at `(row, col)`. + */ + [[nodiscard]] Complex& operator()(std::size_t row, std::size_t col); + + /** + * @brief Const element access. + * @param row Row index in `[0, K_ROWS)`. + * @param col Column index in `[0, K_COLS)`. + * @return Copy of the element at `(row, col)`. + */ + [[nodiscard]] Complex operator()(std::size_t row, std::size_t col) const; + + /** + * @brief Matrix product `*this * rhs`. + * @param rhs Right-hand factor. + * @return Product of the two matrices. + */ + [[nodiscard]] Matrix2x2 operator*(const Matrix2x2& rhs) const; + + /** + * @brief Returns the conjugate transpose (adjoint) of this matrix. + * @return Adjoint matrix `A^\dagger`. + */ + [[nodiscard]] Matrix2x2 adjoint() const; + + /** + * @brief Returns the trace of this matrix. + * @return Sum of diagonal entries. + */ + [[nodiscard]] Complex trace() const; + + /** + * @brief Returns the determinant of this matrix. + * @return Complex determinant `ad - bc`. + */ + [[nodiscard]] Complex determinant() const; + + /** + * @brief Checks approximate equality using an absolute entry-wise tolerance. + * + * For each entry `i`, the comparison uses `|a_i - b_i| <= tol`, where the + * absolute value is the complex modulus. + * + * @param other Matrix to compare against. + * @param tol Maximum allowed complex modulus of each entry difference. + * @return True if every entry differs by at most @p tol. + */ + [[nodiscard]] bool isApprox(const Matrix2x2& other, + double tol = MATRIX_TOLERANCE) const; +}; + +/** + * @brief Fixed-size 4x4 matrix in row-major layout. + * + * Used to represent two-qubit gate unitaries. Elements are stored in a flat + * array with index `(row * K_COLS) + col`. + */ +struct Matrix4x4 { + /// Number of rows. + static constexpr std::size_t K_ROWS = 4; + /// Number of columns. + static constexpr std::size_t K_COLS = 4; + /// Total number of stored elements. + static constexpr std::size_t K_SIZE_AT_COMPILE_TIME = 16; + + /// Flat row-major storage of all matrix entries. + std::array data{}; + + /** + * @brief Constructs a matrix from its sixteen row-major entries. + * @param m00 Element at row 0, column 0. + * @param m01 Element at row 0, column 1. + * @param m02 Element at row 0, column 2. + * @param m03 Element at row 0, column 3. + * @param m10 Element at row 1, column 0. + * @param m11 Element at row 1, column 1. + * @param m12 Element at row 1, column 2. + * @param m13 Element at row 1, column 3. + * @param m20 Element at row 2, column 0. + * @param m21 Element at row 2, column 1. + * @param m22 Element at row 2, column 2. + * @param m23 Element at row 2, column 3. + * @param m30 Element at row 3, column 0. + * @param m31 Element at row 3, column 1. + * @param m32 Element at row 3, column 2. + * @param m33 Element at row 3, column 3. + * @return A new `Matrix4x4` with the given elements. + */ + [[nodiscard]] static Matrix4x4 + fromElements(const Complex& m00, const Complex& m01, const Complex& m02, + const Complex& m03, const Complex& m10, const Complex& m11, + const Complex& m12, const Complex& m13, const Complex& m20, + const Complex& m21, const Complex& m22, const Complex& m23, + const Complex& m30, const Complex& m31, const Complex& m32, + const Complex& m33); + + /** + * @brief Returns the 4x4 identity matrix. + * @return Identity matrix with ones on the diagonal. + */ + [[nodiscard]] static constexpr Matrix4x4 identity() { + return {{1, 0, 0, 0, // row 0 + 0, 1, 0, 0, // row 1 + 0, 0, 1, 0, // row 2 + 0, 0, 0, 1}}; // row 3 + } + + /** + * @brief Mutable element access. + * @param row Row index in `[0, K_ROWS)`. + * @param col Column index in `[0, K_COLS)`. + * @return Reference to the element at `(row, col)`. + */ + [[nodiscard]] Complex& operator()(std::size_t row, std::size_t col); + + /** + * @brief Const element access. + * @param row Row index in `[0, K_ROWS)`. + * @param col Column index in `[0, K_COLS)`. + * @return Copy of the element at `(row, col)`. + */ + [[nodiscard]] Complex operator()(std::size_t row, std::size_t col) const; + + /** + * @brief Matrix product `*this * rhs`. + * @param rhs Right-hand factor. + * @return Product of the two matrices. + */ + [[nodiscard]] Matrix4x4 operator*(const Matrix4x4& rhs) const; + + /** + * @brief Returns the conjugate transpose (adjoint) of this matrix. + * @return Adjoint matrix `A^\dagger`. + */ + [[nodiscard]] Matrix4x4 adjoint() const; + + /** + * @brief Returns the trace of this matrix. + * @return Sum of diagonal entries. + */ + [[nodiscard]] Complex trace() const; + + /** + * @brief Returns the determinant of this matrix. + * @return Complex determinant computed via Laplace expansion. + */ + [[nodiscard]] Complex determinant() const; + + /** + * @brief Checks approximate equality using an absolute entry-wise tolerance. + * + * For each entry `i`, the comparison uses `|a_i - b_i| <= tol`, where the + * absolute value is the complex modulus. + * + * @param other Matrix to compare against. + * @param tol Maximum allowed complex modulus of each entry difference. + * @return True if every entry differs by at most @p tol. + */ + [[nodiscard]] bool isApprox(const Matrix4x4& other, + double tol = MATRIX_TOLERANCE) const; +}; + +/** + * @brief Square matrix with runtime dimension. + * + * Used when the Hilbert-space dimension depends on the operation, for example, + * in controlled gates (`CtrlOp`) and inverses (`InvOp`). Storage is row-major + * and held behind a private implementation pointer. + */ +class DynamicMatrix { +public: + /// Creates an empty 0x0 matrix. + DynamicMatrix(); + + /** + * @brief Creates a zero-initialized square matrix. + * @param dim Side length of the square matrix. + */ + explicit DynamicMatrix(std::int64_t dim); + + /// Copy constructor. + DynamicMatrix(const DynamicMatrix& other); + /// Move constructor. + DynamicMatrix(DynamicMatrix&& other) noexcept; + /// Copy assignment. + DynamicMatrix& operator=(const DynamicMatrix& other); + /// Move assignment. + DynamicMatrix& operator=(DynamicMatrix&& other) noexcept; + /// Destructor. + ~DynamicMatrix(); + + /** + * @brief Returns a square identity matrix of the given dimension. + * @param dim Side length of the identity matrix. + * @return Identity matrix with ones on the diagonal. + */ + [[nodiscard]] static DynamicMatrix identity(std::int64_t dim); + + /** + * @brief Returns the number of rows. + * @return Matrix dimension. + */ + [[nodiscard]] std::int64_t rows() const; + + /** + * @brief Returns the number of columns. + * @return Matrix dimension. + */ + [[nodiscard]] std::int64_t cols() const; + + /** + * @brief Mutable element access. + * @param row Row index in `[0, dim)`. + * @param col Column index in `[0, dim)`. + * @return Reference to the element at `(row, col)`. + */ + [[nodiscard]] Complex& operator()(std::int64_t row, std::int64_t col); + + /** + * @brief Const element access. + * @param row Row index in `[0, dim)`. + * @param col Column index in `[0, dim)`. + * @return Copy of the element at `(row, col)`. + */ + [[nodiscard]] Complex operator()(std::int64_t row, std::int64_t col) const; + + /** + * @brief Copies a 2x2 block into the bottom-right corner. + * @param block Source block placed at indices `(dim-2, dim-2)` through + * `(dim-1, dim-1)`. + */ + void setBottomRightCorner(const Matrix2x2& block); + + /** + * @brief Copies a 4x4 block into the bottom-right corner. + * @param block Source block placed at indices `(dim-4, dim-4)` through + * `(dim-1, dim-1)`. + */ + void setBottomRightCorner(const Matrix4x4& block); + + /** + * @brief Copies a dynamic block into the bottom-right corner. + * @param block Source block placed at indices `(dim - block.rows(), ...)` + * through + * `(dim-1, dim-1)`. + */ + void setBottomRightCorner(const DynamicMatrix& block); + + /** + * @brief Returns the conjugate transpose (adjoint) of this matrix. + * @return Adjoint matrix `A^\dagger`. + */ + [[nodiscard]] DynamicMatrix adjoint() const; + + /** + * @brief Replaces this matrix with a copy of a 1x1 matrix. + * @param src Source matrix. + */ + void assignFrom(const Matrix1x1& src); + + /** + * @brief Replaces this matrix with a copy of a 2x2 matrix. + * @param src Source matrix. + */ + void assignFrom(const Matrix2x2& src); + + /** + * @brief Replaces this matrix with a copy of a 4x4 matrix. + * @param src Source matrix. + */ + void assignFrom(const Matrix4x4& src); + + /** + * @brief Replaces this matrix with a copy of another dynamic matrix. + * @param src Source matrix. + */ + void assignFrom(const DynamicMatrix& src); + + /** + * @brief Checks approximate equality against a fixed 2x2 matrix. + * + * Returns false if this matrix is not 2x2. + * + * @param other Fixed-size matrix to compare against. + * @param tol Maximum allowed complex modulus of each entry difference. + * @return True if dimensions match and every entry differs by at most @p tol. + */ + [[nodiscard]] bool isApprox(const Matrix2x2& other, + double tol = MATRIX_TOLERANCE) const; + + /** + * @brief Checks approximate equality against a fixed 4x4 matrix. + * + * Returns false if this matrix is not 4x4. + * + * @param other Fixed-size matrix to compare against. + * @param tol Maximum allowed complex modulus of each entry difference. + * @return True if dimensions match and every entry differs by at most @p tol. + */ + [[nodiscard]] bool isApprox(const Matrix4x4& other, + double tol = MATRIX_TOLERANCE) const; + + /** + * @brief Checks approximate equality against another dynamic matrix. + * + * Returns false if the dimensions differ. + * + * @param other Matrix to compare against. + * @param tol Maximum allowed complex modulus of each entry difference. + * @return True if dimensions match and every entry differs by at most @p tol. + */ + [[nodiscard]] bool isApprox(const DynamicMatrix& other, + double tol = MATRIX_TOLERANCE) const; + +private: + struct Impl; + std::unique_ptr impl_; +}; + +/** + * @brief Type trait for the four supported matrix types. + * + * True for @ref Matrix1x1, @ref Matrix2x2, @ref Matrix4x4, and @ref + * DynamicMatrix. + * + * @tparam T Candidate type. + */ +template +inline constexpr bool + is_supported_matrix_v = // NOLINT(readability-identifier-naming) + std::disjunction_v, std::is_same, + std::is_same, + std::is_same>; +} // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt index f33ece7000..cb7c30a4ee 100644 --- a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt @@ -24,13 +24,13 @@ add_mlir_dialect_library( MLIRQCOOpsIncGen MLIRQCOInterfacesIncGen LINK_LIBS + PUBLIC + MLIRQCOMatrix PRIVATE MLIRIR MLIRArithDialect MLIRInferTypeOpInterface - MLIRSideEffectInterfaces - PUBLIC - Eigen3::Eigen) + MLIRSideEffectInterfaces) mqt_mlir_target_use_project_options(MLIRQCODialect) diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp index 25fc88d084..ccfb9b6093 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp @@ -10,6 +10,7 @@ #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include #include @@ -340,12 +341,12 @@ void CtrlOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional CtrlOp::getUnitaryMatrix() { +std::optional CtrlOp::getUnitaryMatrix() { auto&& bodyUnitary = getBodyUnitary(); if (!bodyUnitary) { return std::nullopt; } - auto&& targetMatrix = bodyUnitary.getUnitaryMatrix(); + auto&& targetMatrix = bodyUnitary.getUnitaryMatrix(); if (!targetMatrix) { return std::nullopt; } @@ -354,15 +355,20 @@ std::optional CtrlOp::getUnitaryMatrix() { const auto targetDim = targetMatrix->cols(); assert(targetMatrix->cols() == targetMatrix->rows()); + if (getNumControls() >= 32) { + llvm::reportFatalUsageError( + "Creating the unitary matrix for a CtrlOp with more than 31 controls " + "is not supported due to memory constraints."); + } + // define dimensions and type of output matrix - assert(getNumControls() < sizeof(unsigned long long) * 8); const auto dim = static_cast((1ULL << getNumControls()) * targetDim); // initialize result with identity - Eigen::MatrixXcd matrix = Eigen::MatrixXcd::Identity(dim, dim); + auto matrix = DynamicMatrix::identity(dim); // apply target matrix - matrix.bottomRightCorner(targetDim, targetDim) = *targetMatrix; + matrix.setBottomRightCorner(*targetMatrix); return matrix; } diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp index d82a64f819..02a9204941 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -401,17 +401,15 @@ void InvOp::getCanonicalizationPatterns(RewritePatternSet& results, CancelNestedInv>(context); } -std::optional InvOp::getUnitaryMatrix() { +std::optional InvOp::getUnitaryMatrix() { auto&& bodyUnitary = getBodyUnitary(); if (!bodyUnitary) { return std::nullopt; } - auto&& targetMatrix = bodyUnitary.getUnitaryMatrix(); + const auto targetMatrix = bodyUnitary.getUnitaryMatrix(); if (!targetMatrix) { return std::nullopt; } - targetMatrix->adjointInPlace(); - - return targetMatrix; + return targetMatrix->adjoint(); } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp index fc6a3c1e34..60cb98c196 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -43,9 +43,9 @@ void DCXOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix4cd DCXOp::getUnitaryMatrix() { - return Eigen::Matrix4cd{{1, 0, 0, 0}, // row 0 - {0, 0, 1, 0}, // row 1 - {0, 0, 0, 1}, // row 2 - {0, 1, 0, 0}}; // row 3 +Matrix4x4 DCXOp::getUnitaryMatrix() { + return Matrix4x4::fromElements(1, 0, 0, 0, // row 0 + 0, 0, 1, 0, // row 1 + 0, 0, 0, 1, // row 2 + 0, 1, 0, 0); // row 3 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ECROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ECROp.cpp index 676082e68f..9223bbe11c 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ECROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ECROp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -44,14 +44,14 @@ void ECROp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix4cd ECROp::getUnitaryMatrix() { +Matrix4x4 ECROp::getUnitaryMatrix() { using namespace std::complex_literals; constexpr auto m0 = 0i; constexpr auto m1 = std::complex{1.0 / std::numbers::sqrt2}; constexpr auto mi = std::complex{0.0, 1.0 / std::numbers::sqrt2}; - return Eigen::Matrix4cd{{m0, m0, m1, mi}, // row 0 - {m0, m0, mi, m1}, // row 1 - {m1, -mi, m0, m0}, // row 2 - {-mi, m1, m0, m0}}; // row 3 + return Matrix4x4::fromElements(m0, m0, m1, mi, // row 0 + m0, m0, mi, m1, // row 1 + m1, -mi, m0, m0, // row 2 + -mi, m1, m0, m0); // row 3 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp index aeb8a5c4b9..c25cbbb344 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp @@ -9,9 +9,9 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -61,10 +61,9 @@ void GPhaseOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional, 1, 1>> -GPhaseOp::getUnitaryMatrix() { +std::optional GPhaseOp::getUnitaryMatrix() { if (const auto theta = valueToDouble(getTheta())) { - return Eigen::Matrix, 1, 1>{std::polar(1.0, *theta)}; + return Matrix1x1::fromElements(std::polar(1.0, *theta)); } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/HOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/HOp.cpp index 2fe55f5a68..602acff7d8 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/HOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/HOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -43,7 +43,8 @@ void HOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd HOp::getUnitaryMatrix() { +Matrix2x2 HOp::getUnitaryMatrix() { constexpr auto x = 1.0 / std::numbers::sqrt2; - return Eigen::Matrix2cd{{x, x}, {x, -1.0 * x}}; + return Matrix2x2::fromElements(x, x, // row 0 + x, -x); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/IdOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/IdOp.cpp index 4cdf785c07..adbe47c592 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/IdOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/IdOp.cpp @@ -9,8 +9,8 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -41,6 +41,7 @@ void IdOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd IdOp::getUnitaryMatrix() { - return Eigen::Matrix2cd{{1, 0}, {0, 1}}; +Matrix2x2 IdOp::getUnitaryMatrix() { + return Matrix2x2::fromElements(1, 0, // row 0 + 0, 1); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp index 08ee6e068e..fa7c5c8e22 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -65,9 +65,10 @@ void POp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional POp::getUnitaryMatrix() { +std::optional POp::getUnitaryMatrix() { if (const auto theta = valueToDouble(getTheta())) { - return Eigen::Matrix2cd{{1.0, 0.0}, {0.0, std::polar(1.0, *theta)}}; + return Matrix2x2::fromElements(1, 0, // row 0 + 0, std::polar(1.0, *theta)); // row 1 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp index 5e7b04b268..1eca6542b0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp @@ -9,9 +9,9 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -80,16 +80,17 @@ void ROp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional ROp::getUnitaryMatrix() { +std::optional ROp::getUnitaryMatrix() { const auto theta = valueToDouble(getTheta()); const auto phi = valueToDouble(getPhi()); if (!theta || !phi) { return std::nullopt; } - const auto thetaSin = std::sin(*theta / 2.0); + const auto thetaSin = std::sin(*theta / 2); const auto m01 = std::polar(thetaSin, -*phi - (std::numbers::pi / 2)); const auto m10 = std::polar(thetaSin, *phi - (std::numbers::pi / 2)); - const std::complex thetaCos = std::cos(*theta / 2.0); - return Eigen::Matrix2cd{{thetaCos, m01}, {m10, thetaCos}}; + const auto thetaCos = std::cos(*theta / 2); + return Matrix2x2::fromElements(thetaCos, m01, // row 0 + m10, thetaCos); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp index eef8bf100c..5a81d94797 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -65,13 +65,14 @@ void RXOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RXOp::getUnitaryMatrix() { +std::optional RXOp::getUnitaryMatrix() { using namespace std::complex_literals; if (const auto theta = valueToDouble(getTheta())) { - const auto m00 = std::cos(*theta / 2.0) + 0i; - const auto m01 = -1i * std::sin(*theta / 2.0); - return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; + const auto diag = std::cos(*theta / 2); + const auto offDiag = -1i * std::sin(*theta / 2); + return Matrix2x2::fromElements(diag, offDiag, // row 0 + offDiag, diag); // row 1 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index 02e3c5d421..c55edc7f8a 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -82,17 +82,17 @@ void RXXOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RXXOp::getUnitaryMatrix() { +std::optional RXXOp::getUnitaryMatrix() { using namespace std::complex_literals; if (const auto theta = valueToDouble(getTheta())) { const auto m0 = 0i; - const auto mc = std::cos(*theta / 2.0) + 0i; - const auto ms = -1i * std::sin(*theta / 2.0); - return Eigen::Matrix4cd{{mc, m0, m0, ms}, // row 0 - {m0, mc, ms, m0}, // row 1 - {m0, ms, mc, m0}, // row 2 - {ms, m0, m0, mc}}; // row 3 + const auto mc = std::cos(*theta / 2); + const auto ms = -1i * std::sin(*theta / 2); + return Matrix4x4::fromElements(mc, m0, m0, ms, // row 0 + m0, mc, ms, m0, // row 1 + m0, ms, mc, m0, // row 2 + ms, m0, m0, mc); // row 3 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp index 3b9255250a..7c58ee3cbd 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -65,11 +64,12 @@ void RYOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RYOp::getUnitaryMatrix() { +std::optional RYOp::getUnitaryMatrix() { if (const auto theta = valueToDouble(getTheta())) { - const auto m00 = std::complex{std::cos(*theta / 2.0)}; - const auto m01 = std::complex{-std::sin(*theta / 2.0)}; - return Eigen::Matrix2cd{{m00, m01}, {-m01, m00}}; + const auto m00 = std::cos(*theta / 2); + const auto m01 = -std::sin(*theta / 2); + return Matrix2x2::fromElements(m00, m01, // row 0 + -m01, m00); // row 1 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp index 2996fdd289..9f59ca471e 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -82,17 +82,16 @@ void RYYOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RYYOp::getUnitaryMatrix() { +std::optional RYYOp::getUnitaryMatrix() { using namespace std::complex_literals; if (const auto theta = valueToDouble(getTheta())) { - const auto m0 = 0i; - const auto mc = std::complex{std::cos(*theta / 2.0)}; - const auto ms = std::complex{0.0, std::sin(*theta / 2.0)}; - return Eigen::Matrix4cd{{mc, m0, m0, ms}, // row 0 - {m0, mc, -ms, m0}, // row 1 - {m0, -ms, mc, m0}, // row 2 - {ms, m0, m0, mc}}; // row 3 + const auto mc = std::cos(*theta / 2); + const auto ms = 1i * std::sin(*theta / 2); + return Matrix4x4::fromElements(mc, 0, 0, ms, // row 0 + 0, mc, -ms, 0, // row 1 + 0, -ms, mc, 0, // row 2 + ms, 0, 0, mc); // row 3 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp index cad399846c..288e9f0d0f 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -65,14 +65,12 @@ void RZOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RZOp::getUnitaryMatrix() { - using namespace std::complex_literals; - +std::optional RZOp::getUnitaryMatrix() { if (const auto theta = valueToDouble(getTheta())) { - const auto m00 = std::polar(1.0, -*theta / 2.0); - const auto m01 = 0i; - const auto m11 = std::polar(1.0, *theta / 2.0); - return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; + const auto m00 = std::polar(1.0, -*theta / 2); + const auto m11 = std::polar(1.0, *theta / 2); + return Matrix2x2::fromElements(m00, 0, // row 0 + 0, m11); // row 1 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp index 9f3ecedfe6..e182a0327b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -69,17 +69,16 @@ void RZXOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RZXOp::getUnitaryMatrix() { +std::optional RZXOp::getUnitaryMatrix() { using namespace std::complex_literals; if (const auto theta = valueToDouble(getTheta())) { - const auto m0 = 0i; - const auto mc = std::complex{std::cos(*theta / 2.0)}; - const auto ms = std::complex{0.0, std::sin(*theta / 2.0)}; - return Eigen::Matrix4cd{{mc, -ms, m0, m0}, // row 0 - {-ms, mc, m0, m0}, // row 1 - {m0, m0, mc, ms}, // row 2 - {m0, m0, ms, mc}}; // row 3 + const auto mc = std::cos(*theta / 2); + const auto ms = 1i * std::sin(*theta / 2); + return Matrix4x4::fromElements(mc, -ms, 0, 0, // row 0 + -ms, mc, 0, 0, // row 1 + 0, 0, mc, ms, // row 2 + 0, 0, ms, mc); // row 3 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp index 8a6793475b..c290dc69b0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -82,17 +82,14 @@ void RZZOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional RZZOp::getUnitaryMatrix() { - using namespace std::complex_literals; - +std::optional RZZOp::getUnitaryMatrix() { if (const auto theta = valueToDouble(getTheta())) { - const auto m0 = 0i; - const auto mp = std::polar(1.0, *theta / 2.0); - const auto mm = std::polar(1.0, -*theta / 2.0); - return Eigen::Matrix4cd{{mm, m0, m0, m0}, // row 0 - {m0, mp, m0, m0}, // row 1 - {m0, m0, mp, m0}, // row 2 - {m0, m0, m0, mm}}; // row 3 + const auto mp = std::polar(1.0, *theta / 2); + const auto mm = std::polar(1.0, -*theta / 2); + return Matrix4x4::fromElements(mm, 0, 0, 0, // row 0 + 0, mp, 0, 0, // row 1 + 0, 0, mp, 0, // row 2 + 0, 0, 0, mm); // row 3 } return std::nullopt; } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp index e557053b4b..768bddfcc0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -55,9 +55,9 @@ void SOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd SOp::getUnitaryMatrix() { +Matrix2x2 SOp::getUnitaryMatrix() { using namespace std::complex_literals; - return Eigen::Matrix2cd{{1.0, 0.0}, // row 0 - {0.0, 1i}}; // row 1 + return Matrix2x2::fromElements(1, 0, // row 0 + 0, 1i); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp index b0cb2907e8..673ffffdee 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -55,9 +55,9 @@ void SWAPOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix4cd SWAPOp::getUnitaryMatrix() { - return Eigen::Matrix4cd{{1, 0, 0, 0}, // row 0 - {0, 0, 1, 0}, // row 1 - {0, 1, 0, 0}, // row 2 - {0, 0, 0, 1}}; // row 3 +Matrix4x4 SWAPOp::getUnitaryMatrix() { + return Matrix4x4::fromElements(1, 0, 0, 0, // row 0 + 0, 0, 1, 0, // row 1 + 0, 1, 0, 0, // row 2 + 0, 0, 0, 1); // row 3 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXOp.cpp index 35a26622a2..e42d30f234 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -55,8 +55,9 @@ void SXOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd SXOp::getUnitaryMatrix() { - constexpr auto m00 = std::complex{0.5, 0.5}; - constexpr auto m01 = std::complex{0.5, -0.5}; - return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; +Matrix2x2 SXOp::getUnitaryMatrix() { + constexpr auto diag = std::complex{0.5, 0.5}; + constexpr auto offDiag = std::complex{0.5, -0.5}; + return Matrix2x2::fromElements(diag, offDiag, // row 0 + offDiag, diag); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXdgOp.cpp index c77e15cbf0..73b36fcf33 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SXdgOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -56,8 +56,9 @@ void SXdgOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd SXdgOp::getUnitaryMatrix() { - constexpr auto m00 = std::complex{0.5, -0.5}; - constexpr auto m01 = std::complex{0.5, 0.5}; - return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; +Matrix2x2 SXdgOp::getUnitaryMatrix() { + constexpr auto diag = std::complex{0.5, -0.5}; + constexpr auto offDiag = std::complex{0.5, 0.5}; + return Matrix2x2::fromElements(diag, offDiag, // row 0 + offDiag, diag); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp index 8a6cf8606c..1a9110d312 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -55,9 +55,9 @@ void SdgOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd SdgOp::getUnitaryMatrix() { +Matrix2x2 SdgOp::getUnitaryMatrix() { using namespace std::complex_literals; - return Eigen::Matrix2cd{{1.0, 0.0}, // row 0 - {0.0, -1i}}; // row 1 + return Matrix2x2::fromElements(1, 0, // row 0 + 0, -1i); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp index 14afd9814a..8e9a90e582 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -56,7 +56,8 @@ void TOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd TOp::getUnitaryMatrix() { - const auto m11 = std::polar(1.0, std::numbers::pi / 4.0); - return Eigen::Matrix2cd{{1.0, 0.0}, {0.0, m11}}; +Matrix2x2 TOp::getUnitaryMatrix() { + const auto m11 = std::polar(1.0, std::numbers::pi / 4); + return Matrix2x2::fromElements(1, 0, // row 0 + 0, m11); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp index 21a2a07b23..ca093fd81d 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -57,7 +57,8 @@ void TdgOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd TdgOp::getUnitaryMatrix() { - const auto m11 = std::polar(1.0, -std::numbers::pi / 4.0); - return Eigen::Matrix2cd{{1.0, 0.0}, {0.0, m11}}; +Matrix2x2 TdgOp::getUnitaryMatrix() { + const auto m11 = std::polar(1.0, -std::numbers::pi / 4); + return Matrix2x2::fromElements(1, 0, // row 0 + 0, m11); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp index 63158dfdb8..92face4e40 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp @@ -9,9 +9,9 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -105,19 +105,17 @@ void U2Op::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional U2Op::getUnitaryMatrix() { - using namespace std::complex_literals; - +std::optional U2Op::getUnitaryMatrix() { const auto phi = valueToDouble(getPhi()); const auto lambda = valueToDouble(getLambda()); if (!phi || !lambda) { return std::nullopt; } - const auto m00 = 1.0 / std::numbers::sqrt2 + 0i; - const auto m01 = - std::polar(1.0 / std::numbers::sqrt2, *lambda + std::numbers::pi); - const auto m10 = std::polar(1.0 / std::numbers::sqrt2, *phi); - const auto m11 = std::polar(1.0 / std::numbers::sqrt2, *phi + *lambda); - return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; + constexpr auto m00 = 1 / std::numbers::sqrt2; + const auto m01 = std::polar(m00, *lambda + std::numbers::pi); + const auto m10 = std::polar(m00, *phi); + const auto m11 = std::polar(m00, *phi + *lambda); + return Matrix2x2::fromElements(m00, m01, // row 0 + m10, m11); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp index 3a5e34dddd..f504ba3a49 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp @@ -9,9 +9,9 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -125,9 +125,7 @@ void UOp::getCanonicalizationPatterns(RewritePatternSet& results, context); } -std::optional UOp::getUnitaryMatrix() { - using namespace std::complex_literals; - +std::optional UOp::getUnitaryMatrix() { const auto theta = valueToDouble(getTheta()); const auto phi = valueToDouble(getPhi()); const auto lambda = valueToDouble(getLambda()); @@ -135,11 +133,12 @@ std::optional UOp::getUnitaryMatrix() { return std::nullopt; } - const auto c = std::cos(*theta / 2.0); - const auto s = std::sin(*theta / 2.0); - const auto m00 = c + 0i; + const auto c = std::cos(*theta / 2); + const auto s = std::sin(*theta / 2); + const auto m01 = std::polar(s, *lambda + std::numbers::pi); const auto m10 = std::polar(s, *phi); const auto m11 = std::polar(c, *phi + *lambda); - return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; + return Matrix2x2::fromElements(c, m01, // row 0 + m10, m11); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XOp.cpp index bd6903685e..7d53d6f83f 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -41,6 +41,7 @@ void XOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd XOp::getUnitaryMatrix() { - return Eigen::Matrix2cd{{0, 1}, {1, 0}}; +Matrix2x2 XOp::getUnitaryMatrix() { + return Matrix2x2::fromElements(0, 1, // row 0 + 1, 0); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index 6bd93add60..26d5966d11 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -9,9 +9,9 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -101,23 +101,19 @@ void XXMinusYYOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional XXMinusYYOp::getUnitaryMatrix() { - using namespace std::complex_literals; - +std::optional XXMinusYYOp::getUnitaryMatrix() { const auto theta = valueToDouble(getTheta()); const auto beta = valueToDouble(getBeta()); if (!theta || !beta) { return std::nullopt; } - const auto m0 = 0.0 + 0i; - const auto m1 = 1.0 + 0i; - const auto mc = std::cos(*theta / 2.0) + 0i; - const auto s = std::sin(*theta / 2.0); - const auto msp = std::polar(s, *beta - (std::numbers::pi / 2.)); - const auto msm = std::polar(s, -*beta - (std::numbers::pi / 2.)); - return Eigen::Matrix4cd{{mc, m0, m0, msm}, // row 0 - {m0, m1, m0, m0}, // row 1 - {m0, m0, m1, m0}, // row 2 - {msp, m0, m0, mc}}; // row 3 + const auto mc = std::cos(*theta / 2); + const auto s = std::sin(*theta / 2); + const auto msp = std::polar(s, *beta - (std::numbers::pi / 2)); + const auto msm = std::polar(s, -*beta - (std::numbers::pi / 2)); + return Matrix4x4::fromElements(mc, 0, 0, msm, // row 0 + 0, 1, 0, 0, // row 1 + 0, 0, 1, 0, // row 2 + msp, 0, 0, mc); // row 3 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 63648cdcf3..6b4c5f0b9e 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -9,9 +9,9 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include @@ -101,23 +101,19 @@ void XXPlusYYOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -std::optional XXPlusYYOp::getUnitaryMatrix() { - using namespace std::complex_literals; - +std::optional XXPlusYYOp::getUnitaryMatrix() { const auto theta = valueToDouble(getTheta()); const auto beta = valueToDouble(getBeta()); if (!theta || !beta) { return std::nullopt; } - const auto m0 = 0.0 + 0i; - const auto m1 = 1.0 + 0i; - const auto mc = std::cos(*theta / 2.0) + 0i; - const auto s = std::sin(*theta / 2.0); + const auto mc = std::cos(*theta / 2); + const auto s = std::sin(*theta / 2); const auto msp = std::polar(s, *beta - (std::numbers::pi / 2)); const auto msm = std::polar(s, -*beta - (std::numbers::pi / 2)); - return Eigen::Matrix4cd{{m1, m0, m0, m0}, // row 0 - {m0, mc, msp, m0}, // row 1 - {m0, msm, mc, m0}, // row 2 - {m0, m0, m0, m1}}; // row 3 + return Matrix4x4::fromElements(1, 0, 0, 0, // row 0 + 0, mc, msp, 0, // row 1 + 0, msm, mc, 0, // row 2 + 0, 0, 0, 1); // row 3 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/YOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/YOp.cpp index c6607fdf25..e6921dc9f7 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/YOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/YOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -43,8 +43,9 @@ void YOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd YOp::getUnitaryMatrix() { +Matrix2x2 YOp::getUnitaryMatrix() { using namespace std::complex_literals; - return Eigen::Matrix2cd{{0, -1i}, {1i, 0}}; + return Matrix2x2::fromElements(0, -1i, // row 0 + 1i, 0); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ZOp.cpp index 4049201bbc..0e938206f0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ZOp.cpp @@ -10,8 +10,8 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" -#include #include #include #include @@ -41,7 +41,7 @@ void ZOp::getCanonicalizationPatterns(RewritePatternSet& results, results.add(context); } -Eigen::Matrix2cd ZOp::getUnitaryMatrix() { - return Eigen::Matrix2cd{{1.0, 0.0}, // row 0 - {0.0, -1.0}}; // row 1 +Matrix2x2 ZOp::getUnitaryMatrix() { + return Matrix2x2::fromElements(1, 0, // row 0 + 0, -1); // row 1 } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/iSWAPOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/iSWAPOp.cpp index a4642861a4..0f9f189821 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/iSWAPOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/iSWAPOp.cpp @@ -9,19 +9,18 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" - -#include +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include using namespace mlir; using namespace mlir::qco; -Eigen::Matrix4cd iSWAPOp::getUnitaryMatrix() { +Matrix4x4 iSWAPOp::getUnitaryMatrix() { using namespace std::complex_literals; - return Eigen::Matrix4cd{{1, 0, 0, 0}, // row 0 - {0, 0, 1i, 0}, // row 1 - {0, 1i, 0, 0}, // row 2 - {0, 0, 0, 1}}; // row 3 + return Matrix4x4::fromElements(1, 0, 0, 0, // row 0 + 0, 0, 1i, 0, // row 1 + 0, 1i, 0, 0, // row 2 + 0, 0, 0, 1); // row 3 } diff --git a/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt b/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt index 9b94885f1b..6e0d1eed9c 100644 --- a/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt @@ -6,10 +6,19 @@ # # Licensed under the MIT License +add_mlir_library(MLIRQCOMatrix PARTIAL_SOURCES_INTENDED Matrix.cpp LINK_LIBS PUBLIC MLIRSupport) + +mqt_mlir_target_use_project_options(MLIRQCOMatrix) + +target_sources(MLIRQCOMatrix PUBLIC FILE_SET HEADERS BASE_DIRS ${MQT_MLIR_SOURCE_INCLUDE_DIR} FILES + ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/QCO/Utils/Matrix.h) + file(GLOB_RECURSE UTILS_CPP "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +list(FILTER UTILS_CPP EXCLUDE REGEX "Matrix\\.cpp$") add_mlir_dialect_library( MLIRQCOUtils + PARTIAL_SOURCES_INTENDED ${UTILS_CPP} ADDITIONAL_HEADER_DIRS ${PROJECT_SOURCE_DIR}/mlir/include/mlir/Dialect/QCO @@ -24,6 +33,7 @@ mqt_mlir_target_use_project_options(MLIRQCOUtils) # collect header files file(GLOB_RECURSE UTILS_HEADERS_SOURCE "${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/QCO/Utils/*.h") +list(FILTER UTILS_HEADERS_SOURCE EXCLUDE REGEX "Matrix\\.h$") file(GLOB_RECURSE UTILS_HEADERS_BUILD "${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Dialect/QCO/Utils/*.inc") # add public headers using file sets diff --git a/mlir/lib/Dialect/QCO/Utils/Matrix.cpp b/mlir/lib/Dialect/QCO/Utils/Matrix.cpp new file mode 100644 index 0000000000..01d1b35ea4 --- /dev/null +++ b/mlir/lib/Dialect/QCO/Utils/Matrix.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/Utils/Matrix.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco { + +/// Returns true if every entry pair differs by at most @p tol (complex +/// modulus). +[[nodiscard]] static bool entriesAreApprox(ArrayRef lhs, + ArrayRef rhs, double tol) { + return std::ranges::equal(lhs, rhs, + [tol](const Complex& a, const Complex& b) { + return std::abs(a - b) <= tol; + }); +} + +/// Writes the conjugate transpose of @p in into @p out (square, row-major). +static void adjointInto(ArrayRef in, MutableArrayRef out, + const std::size_t dim) { + for (std::size_t row = 0; row < dim; ++row) { + for (std::size_t col = 0; col < dim; ++col) { + out[(row * dim) + col] = std::conj(in[(col * dim) + row]); + } + } +} + +template +static void assignFixedImpl(std::int64_t& dim, SmallVector& data, + const std::array& src) { + dim = static_cast(Dim); + data.assign(src.begin(), src.end()); +} + +template +[[nodiscard]] static bool +isApproxFixedImpl(const std::int64_t dim, ArrayRef data, + const std::array& other, const double tol) { + if (std::cmp_not_equal(dim, Dim)) { + return false; + } + return entriesAreApprox(data, other, tol); +} + +/// Returns @p dim as `size_t` after asserting it is non-negative and squarable. +[[nodiscard]] static std::size_t checkedDim(const std::int64_t dim) { + assert(dim >= 0 && "DynamicMatrix dimension must be non-negative"); + const auto udim = static_cast(dim); + assert(udim == 0 || udim <= std::numeric_limits::max() / udim); + return udim; +} + +[[nodiscard]] static std::size_t checkedStorageSize(const std::int64_t dim) { + const auto udim = checkedDim(dim); + return udim * udim; +} + +static void validateCornerDims(const std::int64_t matrixDim, + const std::int64_t blockDim) { + assert(matrixDim >= 0 && blockDim >= 0 && blockDim <= matrixDim && + "block must fit in the bottom-right corner of the matrix"); + std::ignore = checkedDim(matrixDim); +} + +/// Copies @p blockData into the bottom-right @p blockDim x @p blockDim corner. +static void copyBottomRightCorner(const std::int64_t matrixDim, + MutableArrayRef matrixData, + const std::int64_t blockDim, + ArrayRef blockData) { + validateCornerDims(matrixDim, blockDim); + assert(matrixData.size() >= checkedStorageSize(matrixDim)); + assert(blockData.size() >= checkedStorageSize(blockDim)); + const std::int64_t offset = matrixDim - blockDim; + for (std::int64_t row = 0; row < blockDim; ++row) { + for (std::int64_t col = 0; col < blockDim; ++col) { + matrixData[static_cast(((offset + row) * matrixDim) + + offset + col)] = + blockData[static_cast((row * blockDim) + col)]; + } + } +} + +struct DynamicMatrix::Impl { + std::int64_t dim = 0; + SmallVector data; +}; + +Matrix1x1 Matrix1x1::fromElements(const Complex m00) { return {m00}; } + +Complex& Matrix1x1::operator()(const std::size_t row, const std::size_t col) { + assert(row == 0 && col == 0); + return value; +} + +Complex Matrix1x1::operator()(const std::size_t row, + const std::size_t col) const { + assert(row == 0 && col == 0); + return value; +} + +bool Matrix1x1::isApprox(const Matrix1x1& other, const double tol) const { + return std::abs(value - other.value) <= tol; +} + +Matrix2x2 Matrix2x2::fromElements(const Complex& m00, const Complex& m01, + const Complex& m10, const Complex& m11) { + return {{m00, m01, m10, m11}}; +} + +Complex& Matrix2x2::operator()(const std::size_t row, const std::size_t col) { + return data[(row * K_COLS) + col]; +} + +Complex Matrix2x2::operator()(const std::size_t row, + const std::size_t col) const { + return data[(row * K_COLS) + col]; +} + +Matrix2x2 Matrix2x2::operator*(const Matrix2x2& rhs) const { + return fromElements(data[0] * rhs.data[0] + data[1] * rhs.data[2], + data[0] * rhs.data[1] + data[1] * rhs.data[3], + data[2] * rhs.data[0] + data[3] * rhs.data[2], + data[2] * rhs.data[1] + data[3] * rhs.data[3]); +} + +Matrix2x2 Matrix2x2::adjoint() const { + return fromElements(std::conj(data[0]), std::conj(data[2]), + std::conj(data[1]), std::conj(data[3])); +} + +Complex Matrix2x2::trace() const { return data[0] + data[3]; } + +Complex Matrix2x2::determinant() const { + return data[0] * data[3] - data[1] * data[2]; +} + +bool Matrix2x2::isApprox(const Matrix2x2& other, const double tol) const { + return entriesAreApprox(data, other.data, tol); +} + +Matrix4x4 Matrix4x4::fromElements(const Complex& m00, const Complex& m01, + const Complex& m02, const Complex& m03, + const Complex& m10, const Complex& m11, + const Complex& m12, const Complex& m13, + const Complex& m20, const Complex& m21, + const Complex& m22, const Complex& m23, + const Complex& m30, const Complex& m31, + const Complex& m32, const Complex& m33) { + return {{m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, + m32, m33}}; +} + +Complex& Matrix4x4::operator()(const std::size_t row, const std::size_t col) { + return data[(row * K_COLS) + col]; +} + +Complex Matrix4x4::operator()(const std::size_t row, + const std::size_t col) const { + return data[(row * K_COLS) + col]; +} + +Matrix4x4 Matrix4x4::operator*(const Matrix4x4& rhs) const { + Matrix4x4 out{}; + for (std::size_t row = 0; row < K_ROWS; ++row) { + const std::size_t rowBase = row * K_COLS; + const Complex& a0 = data[rowBase + 0]; + const Complex& a1 = data[rowBase + 1]; + const Complex& a2 = data[rowBase + 2]; + const Complex& a3 = data[rowBase + 3]; + out.data[rowBase + 0] = a0 * rhs.data[0] + a1 * rhs.data[4] + + a2 * rhs.data[8] + a3 * rhs.data[12]; + out.data[rowBase + 1] = a0 * rhs.data[1] + a1 * rhs.data[5] + + a2 * rhs.data[9] + a3 * rhs.data[13]; + out.data[rowBase + 2] = a0 * rhs.data[2] + a1 * rhs.data[6] + + a2 * rhs.data[10] + a3 * rhs.data[14]; + out.data[rowBase + 3] = a0 * rhs.data[3] + a1 * rhs.data[7] + + a2 * rhs.data[11] + a3 * rhs.data[15]; + } + return out; +} + +Matrix4x4 Matrix4x4::adjoint() const { + Matrix4x4 out{}; + adjointInto(data, out.data, K_ROWS); + return out; +} + +Complex Matrix4x4::trace() const { + return data[0] + data[5] + data[10] + data[15]; +} + +Complex Matrix4x4::determinant() const { + auto det3 = [](const Complex& m00, const Complex& m01, const Complex& m02, + const Complex& m10, const Complex& m11, const Complex& m12, + const Complex& m20, const Complex& m21, const Complex& m22) { + return m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m12 * m20) + + m02 * (m10 * m21 - m11 * m20); + }; + return data[0] * det3(data[5], data[6], data[7], data[9], data[10], data[11], + data[13], data[14], data[15]) - + data[1] * det3(data[4], data[6], data[7], data[8], data[10], data[11], + data[12], data[14], data[15]) + + data[2] * det3(data[4], data[5], data[7], data[8], data[9], data[11], + data[12], data[13], data[15]) - + data[3] * det3(data[4], data[5], data[6], data[8], data[9], data[10], + data[12], data[13], data[14]); +} + +bool Matrix4x4::isApprox(const Matrix4x4& other, const double tol) const { + return entriesAreApprox(data, other.data, tol); +} + +DynamicMatrix::DynamicMatrix() : impl_(std::make_unique()) {} + +DynamicMatrix::DynamicMatrix(const std::int64_t dim) + : impl_(std::make_unique()) { + impl_->dim = dim; + impl_->data.assign(checkedStorageSize(dim), Complex{}); +} + +DynamicMatrix::DynamicMatrix(const DynamicMatrix& other) + : impl_(std::make_unique(*other.impl_)) {} + +DynamicMatrix::DynamicMatrix(DynamicMatrix&& other) noexcept = default; + +DynamicMatrix& DynamicMatrix::operator=(const DynamicMatrix& other) { + if (this != &other) { + *impl_ = *other.impl_; + } + return *this; +} + +DynamicMatrix& +DynamicMatrix::operator=(DynamicMatrix&& other) noexcept = default; + +DynamicMatrix::~DynamicMatrix() = default; + +std::int64_t DynamicMatrix::rows() const { return impl_->dim; } + +std::int64_t DynamicMatrix::cols() const { return impl_->dim; } + +DynamicMatrix DynamicMatrix::identity(const std::int64_t dim) { + DynamicMatrix matrix(dim); + const auto udim = checkedDim(dim); + for (std::size_t i = 0; i < udim; ++i) { + matrix.impl_->data[(i * udim) + i] = 1.0; + } + return matrix; +} + +Complex& DynamicMatrix::operator()(const std::int64_t row, + const std::int64_t col) { + return impl_->data[static_cast((row * impl_->dim) + col)]; +} + +Complex DynamicMatrix::operator()(const std::int64_t row, + const std::int64_t col) const { + return impl_->data[static_cast((row * impl_->dim) + col)]; +} + +void DynamicMatrix::setBottomRightCorner(const Matrix2x2& block) { + copyBottomRightCorner(impl_->dim, impl_->data, + static_cast(Matrix2x2::K_ROWS), + block.data); +} + +void DynamicMatrix::setBottomRightCorner(const Matrix4x4& block) { + copyBottomRightCorner(impl_->dim, impl_->data, + static_cast(Matrix4x4::K_ROWS), + block.data); +} + +void DynamicMatrix::setBottomRightCorner(const DynamicMatrix& block) { + copyBottomRightCorner(impl_->dim, impl_->data, block.impl_->dim, + block.impl_->data); +} + +DynamicMatrix DynamicMatrix::adjoint() const { + DynamicMatrix out(impl_->dim); + adjointInto(impl_->data, out.impl_->data, checkedDim(impl_->dim)); + return out; +} + +void DynamicMatrix::assignFrom(const Matrix1x1& src) { + impl_->dim = 1; + impl_->data.assign({src.value}); +} + +void DynamicMatrix::assignFrom(const Matrix2x2& src) { + assignFixedImpl( + impl_->dim, impl_->data, src.data); +} + +void DynamicMatrix::assignFrom(const Matrix4x4& src) { + assignFixedImpl( + impl_->dim, impl_->data, src.data); +} + +void DynamicMatrix::assignFrom(const DynamicMatrix& src) { + *impl_ = *src.impl_; +} + +bool DynamicMatrix::isApprox(const Matrix2x2& other, const double tol) const { + return isApproxFixedImpl( + impl_->dim, impl_->data, other.data, tol); +} + +bool DynamicMatrix::isApprox(const Matrix4x4& other, const double tol) const { + return isApproxFixedImpl( + impl_->dim, impl_->data, other.data, tol); +} + +bool DynamicMatrix::isApprox(const DynamicMatrix& other, + const double tol) const { + return entriesAreApprox(impl_->data, other.impl_->data, tol); +} + +} // namespace mlir::qco diff --git a/mlir/lib/Dialect/QTensor/Utils/CMakeLists.txt b/mlir/lib/Dialect/QTensor/Utils/CMakeLists.txt index 59cbc69bec..2014cafbe6 100644 --- a/mlir/lib/Dialect/QTensor/Utils/CMakeLists.txt +++ b/mlir/lib/Dialect/QTensor/Utils/CMakeLists.txt @@ -17,7 +17,9 @@ add_mlir_dialect_library( MLIRQTensorOpsIncGen LINK_LIBS PUBLIC - MLIRQCODialect) + MLIRQCODialect + PRIVATE + MLIRSCFDialect) mqt_mlir_target_use_project_options(MLIRQTensorUtils) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp index 24103da18c..91c31f9899 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp @@ -9,6 +9,7 @@ */ #include "TestCaseUtils.h" +#include "dd/DDDefinitions.hpp" #include "dd/GateMatrixDefinitions.hpp" #include "dd/Operations.hpp" #include "dd/Package.hpp" @@ -17,9 +18,9 @@ #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Matrix.h" #include "qco_programs.h" -#include #include #include #include @@ -33,6 +34,20 @@ using namespace mlir; using namespace qco; +[[nodiscard]] static Matrix2x2 matrix2FromFlat(const dd::GateMatrix& def) { + return Matrix2x2::fromElements(def[0], def[1], def[2], def[3]); +} + +template +[[nodiscard]] static Matrix4x4 +matrix4FromDefinition(const Definition& definition) { + return Matrix4x4::fromElements( + definition[0][0], definition[0][1], definition[0][2], definition[0][3], + definition[1][0], definition[1][1], definition[1][2], definition[1][3], + definition[2][0], definition[2][1], definition[2][2], definition[2][3], + definition[3][0], definition[3][1], definition[3][2], definition[3][3]); +} + namespace { struct QCOMatrixTestCase { @@ -73,16 +88,9 @@ TEST_F(QCOMatrixTest, CXOpMatrix) { const auto cxDD = dd::getDD(cx, *dd); const auto definition = cxDD.getMatrix(2); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix->isApprox(eigenDefinition)); + ASSERT_TRUE(matrix->isApprox(expected)); } /// @} @@ -103,16 +111,9 @@ TEST_F(QCOMatrixTest, InverseIswapOpMatrix) { const auto iswapdgDD = dd::getDD(iswapdg, *dd); const auto definition = iswapdgDD.getMatrix(2); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix->isApprox(eigenDefinition)); + ASSERT_TRUE(matrix->isApprox(expected)); } /// @} @@ -125,16 +126,9 @@ TEST_F(QCOMatrixTest, DCXOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::DCX); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -147,16 +141,9 @@ TEST_F(QCOMatrixTest, ECROpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::ECR); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -174,12 +161,9 @@ TEST_F(QCOMatrixTest, GPhaseOpMatrix) { // Get the definition const auto definition = std::polar(1.0, 0.123); // e^(i*0.123) - // Convert it to an Eigen matrix - Eigen::Matrix, 1, 1> eigenDefinition; - eigenDefinition << definition; + const Matrix1x1 expected = Matrix1x1::fromElements(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -192,12 +176,9 @@ TEST_F(QCOMatrixTest, HOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::H); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -210,12 +191,9 @@ TEST_F(QCOMatrixTest, IdOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::I); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -228,16 +206,9 @@ TEST_F(QCOMatrixTest, iSWAPOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::iSWAP); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -255,12 +226,9 @@ TEST_F(QCOMatrixTest, POpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::P, {0.123}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -278,12 +246,9 @@ TEST_F(QCOMatrixTest, ROpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::R, {0.123, 0.456}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -302,12 +267,9 @@ TEST_F(QCOMatrixTest, RXOpMatrix) { const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::RX, {0.123}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -325,16 +287,9 @@ TEST_F(QCOMatrixTest, RXXOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::RXX, {0.123}); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -353,12 +308,9 @@ TEST_F(QCOMatrixTest, RYOpMatrix) { const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::RY, {0.456}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -376,16 +328,9 @@ TEST_F(QCOMatrixTest, RYYOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::RYY, {0.123}); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -404,12 +349,9 @@ TEST_F(QCOMatrixTest, RZOpMatrix) { const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::RZ, {0.789}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -427,16 +369,9 @@ TEST_F(QCOMatrixTest, RZXOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::RZX, {0.123}); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -454,16 +389,9 @@ TEST_F(QCOMatrixTest, RZZOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::RZZ, {0.123}); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -476,12 +404,9 @@ TEST_F(QCOMatrixTest, SOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::S); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -494,12 +419,9 @@ TEST_F(QCOMatrixTest, SdgOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::Sdg); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -512,16 +434,9 @@ TEST_F(QCOMatrixTest, SWAPOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::SWAP); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -534,12 +449,9 @@ TEST_F(QCOMatrixTest, SXOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::SX); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -552,12 +464,9 @@ TEST_F(QCOMatrixTest, SXdgOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::SXdg); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -570,12 +479,9 @@ TEST_F(QCOMatrixTest, TOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::T); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -588,12 +494,9 @@ TEST_F(QCOMatrixTest, TdgOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::Tdg); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -612,12 +515,9 @@ TEST_F(QCOMatrixTest, U2OpMatrix) { const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::U2, {0.234, 0.567}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -636,12 +536,9 @@ TEST_F(QCOMatrixTest, UOpMatrix) { const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::U, {0.1, 0.2, 0.3}); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -654,12 +551,9 @@ TEST_F(QCOMatrixTest, XOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::X); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -678,16 +572,9 @@ TEST_F(QCOMatrixTest, XXMinusYYOpMatrix) { const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::XXminusYY, {0.123, 0.456}); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -706,16 +593,9 @@ TEST_F(QCOMatrixTest, XXPlusYYOp) { const auto definition = dd::opToTwoQubitGateMatrix(qc::OpType::XXplusYY, {0.123, 0.456}); - // Convert it to an Eigen matrix - Eigen::Matrix4cd eigenDefinition; - eigenDefinition << definition[0][0], definition[0][1], definition[0][2], - definition[0][3], definition[1][0], definition[1][1], definition[1][2], - definition[1][3], definition[2][0], definition[2][1], definition[2][2], - definition[2][3], definition[3][0], definition[3][1], definition[3][2], - definition[3][3]; + const Matrix4x4 expected = matrix4FromDefinition(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -728,12 +608,9 @@ TEST_F(QCOMatrixTest, YOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::Y); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} @@ -746,11 +623,8 @@ TEST_F(QCOMatrixTest, ZOpMatrix) { // Get the definition of the matrix from the DD library const auto definition = dd::opToSingleQubitGateMatrix(qc::OpType::Z); - // Convert it to an Eigen matrix - Eigen::Matrix2cd eigenDefinition; - eigenDefinition << definition[0], definition[1], definition[2], definition[3]; + const Matrix2x2 expected = matrix2FromFlat(definition); - // Check if the matrices are equal - ASSERT_TRUE(matrix.isApprox(eigenDefinition)); + ASSERT_TRUE(matrix.isApprox(expected)); } /// @} diff --git a/mlir/unittests/Dialect/QCO/Utils/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Utils/CMakeLists.txt index e5815170b6..ac92f4e9aa 100644 --- a/mlir/unittests/Dialect/QCO/Utils/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Utils/CMakeLists.txt @@ -7,9 +7,9 @@ # Licensed under the MIT License set(qco_utils_target mqt-core-mlir-unittest-qco-utils) -add_executable(${qco_utils_target} test_drivers.cpp test_wireiterator.cpp) +add_executable(${qco_utils_target} test_drivers.cpp test_unitary_matrix.cpp test_wireiterator.cpp) target_link_libraries(${qco_utils_target} PRIVATE GTest::gtest_main MLIRQCOUtils - MLIRQCOProgramBuilder) + MLIRQCOProgramBuilder MLIRQCOMatrix) mqt_mlir_configure_unittest_target(${qco_utils_target}) gtest_discover_tests(${qco_utils_target} PROPERTIES LABELS mqt-mlir-unittests DISCOVERY_TIMEOUT 60) diff --git a/mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp b/mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp new file mode 100644 index 0000000000..8008dc7561 --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/Utils/Matrix.h" + +#include + +#include +#include + +using namespace mlir::qco; +using namespace std::complex_literals; + +static_assert(is_supported_matrix_v); +static_assert(is_supported_matrix_v); +static_assert(is_supported_matrix_v); +static_assert(is_supported_matrix_v); +static_assert(!is_supported_matrix_v); + +[[nodiscard]] static Matrix2x2 pauliX() { + return Matrix2x2::fromElements(0, 1, 1, 0); +} + +[[nodiscard]] static Matrix4x4 swapMatrix() { + return Matrix4x4::fromElements(1, 0, 0, 0, // row 0 + 0, 0, 1, 0, // row 1 + 0, 1, 0, 0, // row 2 + 0, 0, 0, 1); // row 3 +} + +TEST(UnitaryMatrix1x1, FromElementsAndAccess) { + Matrix1x1 matrix = Matrix1x1::fromElements(Complex{0.5, 0.5}); + EXPECT_EQ(matrix(0, 0), 0.5 + 0.5i); + matrix(0, 0) = -1i; + EXPECT_EQ(matrix.value, -1i); +} + +TEST(UnitaryMatrix1x1, ConstElementAccess) { + const Matrix1x1 matrix = Matrix1x1::fromElements(Complex{0.25, 0.5}); + EXPECT_EQ(matrix(0, 0), (Complex{0.25, 0.5})); +} + +TEST(UnitaryMatrix1x1, IsApprox) { + const Matrix1x1 a = Matrix1x1::fromElements(1.0); + const Matrix1x1 b = Matrix1x1::fromElements(Complex{1.0, 1e-16}); + EXPECT_TRUE(a.isApprox(b)); + EXPECT_FALSE(a.isApprox(Matrix1x1::fromElements(2.0))); + EXPECT_TRUE(a.isApprox(Matrix1x1::fromElements(1.1), 0.2)); +} + +TEST(UnitaryMatrix2x2, IdentityAndAccess) { + const Matrix2x2 identity = Matrix2x2::identity(); + EXPECT_TRUE(identity.isApprox(Matrix2x2::fromElements(1, 0, 0, 1))); + EXPECT_EQ(identity(0, 1), 0.0); + Matrix2x2 mutableMatrix = identity; + mutableMatrix(1, 1) = 2.0; + EXPECT_EQ(mutableMatrix(1, 1), 2.0); +} + +TEST(UnitaryMatrix2x2, MultiplyAdjointTraceDeterminant) { + const Matrix2x2 x = pauliX(); + const Matrix2x2 identity = Matrix2x2::identity(); + + EXPECT_TRUE((x * x).isApprox(identity)); + EXPECT_TRUE((identity * x).isApprox(x)); + EXPECT_TRUE(x.adjoint().isApprox(x)); + EXPECT_EQ(x.trace(), Complex(0.0, 0.0)); + EXPECT_EQ(identity.trace(), Complex(2.0, 0.0)); + EXPECT_EQ(x.determinant(), Complex(-1.0, 0.0)); + EXPECT_EQ(identity.determinant(), Complex(1.0, 0.0)); +} + +TEST(UnitaryMatrix2x2, IsApprox) { + const Matrix2x2 a = Matrix2x2::identity(); + Matrix2x2 b = a; + b(0, 0) += 1e-15; + EXPECT_TRUE(a.isApprox(b)); + EXPECT_FALSE(a.isApprox(pauliX())); +} + +TEST(UnitaryMatrix4x4, IdentityAndAccess) { + const Matrix4x4 identity = Matrix4x4::identity(); + EXPECT_TRUE(identity.isApprox( + Matrix4x4::fromElements(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))); + EXPECT_EQ(identity(2, 2), 1.0); +} + +TEST(UnitaryMatrix4x4, MultiplyAdjointTraceDeterminant) { + const Matrix4x4 swap = swapMatrix(); + const Matrix4x4 identity = Matrix4x4::identity(); + + EXPECT_TRUE((swap * swap).isApprox(identity)); + EXPECT_TRUE(swap.adjoint().isApprox(swap)); + EXPECT_EQ(identity.trace(), Complex(4.0, 0.0)); + EXPECT_EQ(identity.determinant(), Complex(1.0, 0.0)); +} + +TEST(UnitaryMatrix4x4, IsApprox) { + const Matrix4x4 a = Matrix4x4::identity(); + Matrix4x4 b = a; + b(3, 3) += 1e-15; + EXPECT_TRUE(a.isApprox(b)); + EXPECT_FALSE(a.isApprox(swapMatrix())); +} + +TEST(DynamicMatrix, DefaultAndSizedConstruction) { + const DynamicMatrix empty; + EXPECT_EQ(empty.rows(), 0); + EXPECT_EQ(empty.cols(), 0); + + const DynamicMatrix sized(2); + EXPECT_EQ(sized.rows(), 2); + EXPECT_EQ(sized(0, 0), 0.0); + EXPECT_EQ(sized(1, 1), 0.0); +} + +TEST(DynamicMatrix, CopyMoveAssign) { + DynamicMatrix original(2); + original(0, 0) = 1.0; + original(1, 1) = 1.0; + + DynamicMatrix copied(original); + EXPECT_TRUE(copied.isApprox(original)); + + DynamicMatrix moved(std::move(copied)); + EXPECT_TRUE(moved.isApprox(original)); + + const DynamicMatrix assigned = original; + EXPECT_TRUE(assigned.isApprox(original)); + + DynamicMatrix moveAssigned(1); + moveAssigned = std::move(moved); + EXPECT_TRUE(moveAssigned.isApprox(original)); +} + +TEST(DynamicMatrix, IdentityAndElementAccess) { + const DynamicMatrix identity = DynamicMatrix::identity(3); + EXPECT_EQ(identity.rows(), 3); + EXPECT_EQ(identity.cols(), 3); + EXPECT_EQ(identity(0, 0), 1.0); + EXPECT_EQ(identity(1, 2), 0.0); + EXPECT_EQ(identity(2, 2), 1.0); + + DynamicMatrix mutableMatrix = identity; + mutableMatrix(1, 1) = 0.5; + EXPECT_EQ(mutableMatrix(1, 1), 0.5); +} + +TEST(DynamicMatrix, AssignFrom) { + DynamicMatrix dynamic; + + dynamic.assignFrom(Matrix1x1::fromElements(0.25)); + EXPECT_EQ(dynamic.rows(), 1); + EXPECT_EQ(dynamic(0, 0), 0.25); + + dynamic.assignFrom(pauliX()); + EXPECT_TRUE(dynamic.isApprox(pauliX())); + + dynamic.assignFrom(swapMatrix()); + EXPECT_TRUE(dynamic.isApprox(swapMatrix())); + + const DynamicMatrix source = DynamicMatrix::identity(2); + dynamic.assignFrom(source); + EXPECT_TRUE(dynamic.isApprox(source)); +} + +TEST(DynamicMatrix, SetBottomRightCorner) { + const Matrix2x2 x = pauliX(); + const Matrix4x4 swap = swapMatrix(); + + DynamicMatrix with2x2 = DynamicMatrix::identity(4); + with2x2.setBottomRightCorner(x); + EXPECT_EQ(with2x2(0, 0), 1.0); + EXPECT_EQ(with2x2(2, 2), 0.0); + EXPECT_EQ(with2x2(2, 3), 1.0); + EXPECT_EQ(with2x2(3, 2), 1.0); + + DynamicMatrix with4x4 = DynamicMatrix::identity(6); + with4x4.setBottomRightCorner(swap); + EXPECT_EQ(with4x4(0, 0), 1.0); + EXPECT_EQ(with4x4(1, 1), 1.0); + EXPECT_EQ(with4x4(2, 2), 1.0); + EXPECT_EQ(with4x4(3, 4), 1.0); + EXPECT_EQ(with4x4(4, 3), 1.0); + EXPECT_EQ(with4x4(5, 5), 1.0); + + DynamicMatrix block = DynamicMatrix::identity(2); + block(0, 1) = 1i; + DynamicMatrix withDynamic = DynamicMatrix::identity(3); + withDynamic.setBottomRightCorner(block); + EXPECT_EQ(withDynamic(1, 1), 1.0); + EXPECT_EQ(withDynamic(1, 2), 1i); + EXPECT_EQ(withDynamic(2, 1), 0.0); +} + +TEST(DynamicMatrix, Adjoint) { + DynamicMatrix matrix(2); + matrix(0, 0) = 1.0; + matrix(0, 1) = 1i; + matrix(1, 0) = 0.0; + matrix(1, 1) = 1.0; + + const DynamicMatrix adjoint = matrix.adjoint(); + EXPECT_EQ(adjoint(0, 0), 1.0); + EXPECT_EQ(adjoint(0, 1), 0.0); + EXPECT_EQ(adjoint(1, 0), -1i); + EXPECT_EQ(adjoint(1, 1), 1.0); +} + +TEST(DynamicMatrix, IsApproxRejectsMismatchedExtents) { + EXPECT_FALSE(DynamicMatrix::identity(1).isApprox(DynamicMatrix::identity(2))); +} + +TEST(DynamicMatrix, IsApproxOverloads) { + const Matrix2x2 x = pauliX(); + const Matrix4x4 swap = swapMatrix(); + + DynamicMatrix as2x2; + as2x2.assignFrom(x); + EXPECT_TRUE(as2x2.isApprox(x)); + EXPECT_FALSE(as2x2.isApprox(Matrix2x2::identity())); + + DynamicMatrix as4x4; + as4x4.assignFrom(swap); + EXPECT_TRUE(as4x4.isApprox(swap)); + EXPECT_FALSE(as4x4.isApprox(Matrix4x4::identity())); + + DynamicMatrix wrongDim = DynamicMatrix::identity(3); + EXPECT_FALSE(wrongDim.isApprox(x)); + EXPECT_FALSE(wrongDim.isApprox(swap)); + + const DynamicMatrix a = DynamicMatrix::identity(2); + DynamicMatrix b = a; + b(1, 0) += 1e-15; + EXPECT_TRUE(a.isApprox(b)); + EXPECT_FALSE(a.isApprox(DynamicMatrix::identity(3))); +}