Skip to content

Commit ea22c2d

Browse files
taminobpre-commit-ci[bot]burgholzer
authored
✨ Add unitary matrix to QCO dialect (#1426)
## Description This PR implements the feature of the unitary matrix definition for `UnitaryOpInterface` which has been removed from the original dialect rewrite PR in favor of this follow-up. It aims to allow access to the matrix definition of the standard gates via a fast, stack-allocated matrix returned by the function `getUnitaryMatrixDefinition()` in the trait. A dynamic, heap-based matrix (size `Eigen::Dynamic`) is returned by the `UnitaryOpInterface`. This requires a copy at the moment and further evaluations are required if all matrices should be moved to the heap or if there is a way to make use of the draft function `getFastUnitaryMatrix()` which uses a template parameter, but is currently also only accessible via a trait and thus needs a cast to the correct type first. The changes require the newly introduced `Eigen` library for its linear algebra type. For now, it uses the unsupported kronecker product feature which needs to be discussed before merging. "Unsupported" features in Eigen are community-contributed and are not an indication of how mature the code is. However, the maintainers do not provide any API guarantees. ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [ ] The pull request only contains commits that are focused and relevant to this change. - [ ] I have added appropriate tests that cover the new/changed functionality. - [ ] I have updated the documentation to reflect these changes. - [ ] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [ ] I have added migration instructions to the upgrade guide (if needed). - [ ] The changes follow the project's style guidelines and introduce no new warnings. - [ ] The changes are fully tested and pass the CI checks. - [ ] I have reviewed my own code changes. --------- Signed-off-by: burgholzer <burgholzer@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: burgholzer <burgholzer@me.com>
1 parent 37f254b commit ea22c2d

41 files changed

Lines changed: 1226 additions & 79 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,29 @@ option(MQT_CORE_INSTALL "Generate installation instructions for MQT Core"
6363
option(BUILD_MQT_CORE_TESTS "Build tests for the MQT Core project" ${MQT_CORE_MASTER_PROJECT})
6464
option(BUILD_MQT_CORE_SHARED_LIBS "Build MQT Core libraries as shared libraries"
6565
${BUILD_SHARED_LIBS})
66+
option(BUILD_MQT_CORE_MLIR "Build the MLIR submodule of the MQT Core project" ON)
67+
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
68+
set(BUILD_MQT_CORE_MLIR
69+
OFF
70+
CACHE BOOL
71+
"Disable MLIR build on macOS with GCC to avoid ABI issues between STL and libstdc++"
72+
FORCE)
73+
message(
74+
WARNING "Disabling MLIR build on macOS with GCC to avoid ABI issues between STL and libstdc++")
75+
endif()
76+
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
77+
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "17.0.0")
78+
set(BUILD_MQT_CORE_MLIR
79+
OFF
80+
CACHE
81+
BOOL
82+
"Disable MLIR build on macOS with Apple Clang < 17 due to missing complete C++20 support"
83+
FORCE)
84+
message(
85+
WARNING
86+
"Disabling MLIR build on macOS with Apple Clang < 17 due to missing complete C++20 support")
87+
endif()
88+
endif()
6689

6790
# try to determine the project version
6891
include(GetVersion)
@@ -93,29 +116,6 @@ endif()
93116

94117
set(MQT_CORE_TARGET_NAME "mqt-core")
95118

96-
option(BUILD_MQT_CORE_MLIR "Build the MLIR submodule of the MQT Core project" ON)
97-
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
98-
set(BUILD_MQT_CORE_MLIR
99-
OFF
100-
CACHE BOOL
101-
"Disable MLIR build on macOS with GCC to avoid ABI issues between STL and libstdc++"
102-
FORCE)
103-
message(
104-
WARNING "Disabling MLIR build on macOS with GCC to avoid ABI issues between STL and libstdc++")
105-
endif()
106-
if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
107-
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "17.0.0")
108-
set(BUILD_MQT_CORE_MLIR
109-
OFF
110-
CACHE
111-
BOOL
112-
"Disable MLIR build on macOS with Apple Clang < 17 due to missing complete C++20 support"
113-
FORCE)
114-
message(
115-
WARNING
116-
"Disabling MLIR build on macOS with Apple Clang < 17 due to missing complete C++20 support")
117-
endif()
118-
endif()
119119
if(BUILD_MQT_CORE_MLIR)
120120
include(SetupMLIR)
121121
endif()

cmake/ExternalDependencies.cmake

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ if(BUILD_MQT_CORE_BINDINGS)
2222
find_package(nanobind CONFIG REQUIRED)
2323
endif()
2424

25+
if(BUILD_MQT_CORE_MLIR)
26+
set(Eigen_VERSION
27+
5.0.1
28+
CACHE STRING "Eigen version")
29+
set(Eigen_URL
30+
https://gitlab.com/libeigen/eigen/-/archive/${Eigen_VERSION}/eigen-${Eigen_VERSION}.tar.gz)
31+
set(EIGEN_BUILD_TESTING
32+
OFF
33+
CACHE INTERNAL "Disable building Eigen tests")
34+
FetchContent_Declare(Eigen URL ${Eigen_URL} FIND_PACKAGE_ARGS ${Eigen_VERSION})
35+
list(APPEND FETCH_PACKAGES Eigen)
36+
endif()
37+
2538
set(JSON_VERSION
2639
3.12.0
2740
CACHE STRING "nlohmann_json version")

mlir/.clang-tidy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ Checks: |
77
-misc-use-anonymous-namespace,
88
llvm-twine-local,
99
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access
10+
11+
CheckOptions:
12+
- key: misc-include-cleaner.IgnoreHeaders
13+
value: "Eigen/.*;unsupported/Eigen/.*"

mlir/include/mlir/Dialect/QCO/IR/QCODialect.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#pragma clang diagnostic pop
2222
#endif
2323

24+
#include <Eigen/Core>
2425
#include <mlir/Bytecode/BytecodeOpInterface.h>
2526
#include <mlir/Dialect/Arith/IR/Arith.h>
2627
#include <mlir/IR/Value.h>

mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,62 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> {
2828

2929
let cppNamespace = "::mlir::qco";
3030

31+
// Generic implementation body for getUnitaryMatrix methods
32+
defvar unitaryMatrixMethodBody = [{
33+
auto process = [&]<typename MatrixType>(MatrixType&& m) -> bool {
34+
using TargetT = std::remove_cvref_t<decltype(out)>;
35+
using SourceT = std::remove_cvref_t<MatrixType>;
36+
37+
constexpr bool isTargetDynamic =
38+
(TargetT::SizeAtCompileTime == Eigen::Dynamic);
39+
constexpr bool isSourceDynamic =
40+
(SourceT::SizeAtCompileTime == Eigen::Dynamic);
41+
42+
// Case 1: Target is Dynamic. Always accepts source.
43+
if constexpr (isTargetDynamic) {
44+
out = std::forward<MatrixType>(m);
45+
return true;
46+
}
47+
// Case 2: Target is Fixed.
48+
else {
49+
// Case 2a: Source is Dynamic. Runtime dimension check required.
50+
if constexpr (isSourceDynamic) {
51+
if (m.rows() == static_cast<Eigen::Index>(TargetT::RowsAtCompileTime) &&
52+
m.cols() == static_cast<Eigen::Index>(TargetT::ColsAtCompileTime))
53+
[[likely]] {
54+
out = std::forward<MatrixType>(m);
55+
return true;
56+
}
57+
}
58+
// Case 2b: Source is Fixed. Compile-time check.
59+
else if constexpr (static_cast<Eigen::Index>(
60+
SourceT::RowsAtCompileTime) ==
61+
static_cast<Eigen::Index>(
62+
TargetT::RowsAtCompileTime) &&
63+
static_cast<Eigen::Index>(
64+
SourceT::ColsAtCompileTime) ==
65+
static_cast<Eigen::Index>(
66+
TargetT::ColsAtCompileTime)) {
67+
out = std::forward<MatrixType>(m);
68+
return true;
69+
}
70+
}
71+
return false;
72+
};
73+
74+
75+
if constexpr (requires { $_op.getUnitaryMatrix().has_value(); }) {
76+
if (auto&& matrix = $_op.getUnitaryMatrix()) {
77+
return process(std::move(*matrix));
78+
}
79+
return false;
80+
} else if constexpr (requires { $_op.getUnitaryMatrix(); }) {
81+
return process($_op.getUnitaryMatrix());
82+
} else {
83+
llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!");
84+
}
85+
}];
86+
3187
let methods = [
3288
// Qubit accessors
3389
InterfaceMethod<
@@ -107,7 +163,69 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> {
107163
"Returns the base symbol/mnemonic of the operation.",
108164
"StringRef", "getBaseSymbol", (ins)
109165
>,
166+
InterfaceMethod<
167+
"Populates the given 1x1 unitary matrix if possible.",
168+
"bool", "getUnitaryMatrix1x1",
169+
(ins "Eigen::Matrix<std::complex<double>, 1, 1>&":$out),
170+
unitaryMatrixMethodBody
171+
>,
172+
InterfaceMethod<
173+
"Populates the given 2x2 unitary matrix if possible.",
174+
"bool", "getUnitaryMatrix2x2",
175+
(ins "Eigen::Matrix2cd&":$out),
176+
unitaryMatrixMethodBody
177+
>,
178+
InterfaceMethod<
179+
"Populates the given 4x4 unitary matrix if possible.",
180+
"bool", "getUnitaryMatrix4x4",
181+
(ins "Eigen::Matrix4cd&":$out),
182+
unitaryMatrixMethodBody
183+
>,
184+
InterfaceMethod<
185+
"Populates the given dynamic unitary matrix.",
186+
"bool", "getUnitaryMatrixDynamic",
187+
(ins "Eigen::MatrixXcd&":$out),
188+
unitaryMatrixMethodBody
189+
>
110190
];
191+
192+
let extraClassDeclaration = [{
193+
template<typename MatrixType>
194+
std::optional<MatrixType> getUnitaryMatrix() {
195+
MatrixType out;
196+
bool result = false;
197+
198+
// Dispatch to the appropriate fixed-size or dynamic method based on the
199+
// matrix type.
200+
if constexpr (MatrixType::RowsAtCompileTime == 1 &&
201+
MatrixType::ColsAtCompileTime == 1) {
202+
result = this->getUnitaryMatrix1x1(out);
203+
} else if constexpr (MatrixType::RowsAtCompileTime == 2 &&
204+
MatrixType::ColsAtCompileTime == 2) {
205+
result = this->getUnitaryMatrix2x2(out);
206+
} else if constexpr (MatrixType::RowsAtCompileTime == 4 &&
207+
MatrixType::ColsAtCompileTime == 4) {
208+
result = this->getUnitaryMatrix4x4(out);
209+
} else if constexpr (MatrixType::SizeAtCompileTime == Eigen::Dynamic) {
210+
result = this->getUnitaryMatrixDynamic(out);
211+
} else {
212+
// Fallback: Try obtaining dynamic matrix and see if size matches
213+
Eigen::MatrixXcd dynamicOut;
214+
if (this->getUnitaryMatrixDynamic(dynamicOut)) {
215+
if (dynamicOut.rows() == MatrixType::RowsAtCompileTime &&
216+
dynamicOut.cols() == MatrixType::ColsAtCompileTime) {
217+
out = dynamicOut;
218+
result = true;
219+
}
220+
}
221+
}
222+
223+
if (result) {
224+
return out;
225+
}
226+
return std::nullopt;
227+
}
228+
}];
111229
}
112230

113231
#endif // QCO_INTERFACES

0 commit comments

Comments
 (0)